| Index: pylib/gyp/generator/ninja.py
|
| diff --git a/pylib/gyp/generator/ninja.py b/pylib/gyp/generator/ninja.py
|
| index 7c66a1d0a7544eef40a0d46917fc5cb8ed274226..46c41f60d9a1180ae3d379f8b68ce615700b4cf9 100644
|
| --- a/pylib/gyp/generator/ninja.py
|
| +++ b/pylib/gyp/generator/ninja.py
|
| @@ -8,6 +8,7 @@ import gyp.common
|
| import gyp.msvs_emulation
|
| import gyp.system_test
|
| import gyp.xcode_emulation
|
| +import json
|
| import os.path
|
| import re
|
| import subprocess
|
| @@ -216,7 +217,11 @@ class NinjaWriter:
|
|
|
| if self.flavor == 'win':
|
| # Don't use os.path.normpath here. Callers pass in './foo' and expect
|
| - # the result to be runnable, but normpath removes the prefix.
|
| + # the result to be runnable, but normpath removes the prefix. If the
|
| + # variable is $! prefixed on Windows, don't modify it (used for compiler
|
| + # flags, etc.)
|
| + if path.startswith('$!'):
|
| + return path[2:]
|
| return path.replace('/', '\\')
|
| return path
|
|
|
| @@ -303,10 +308,11 @@ class NinjaWriter:
|
| self.target = Target(spec['type'])
|
|
|
| self.is_mac_bundle = gyp.xcode_emulation.IsMacBundle(self.flavor, spec)
|
| + self.xcode_settings = self.msvs_settings = None
|
| if self.flavor == 'mac':
|
| self.xcode_settings = gyp.xcode_emulation.XcodeSettings(spec)
|
| - else:
|
| - self.xcode_settings = None
|
| + if self.flavor == 'win':
|
| + self.msvs_settings = gyp.msvs_emulation.MsvsSettings(spec)
|
|
|
| # Compute predepends for all rules.
|
| # actions_depends is the dependencies this target depends on before running
|
| @@ -351,7 +357,7 @@ class NinjaWriter:
|
| sources = spec.get('sources', []) + extra_sources
|
| if sources:
|
| link_deps = self.WriteSources(
|
| - config_name, config, sources, compile_depends_stamp,
|
| + config_name, config, sources, compile_depends_stamp, spec,
|
| gyp.xcode_emulation.MacPrefixHeader(
|
| self.xcode_settings, self.GypPathToNinja,
|
| lambda path, lang: self.GypPathToUniqueOutput(path + '-' + lang)))
|
| @@ -435,14 +441,24 @@ class NinjaWriter:
|
| action.get('message', None),
|
| name)
|
| rule_name = self.WriteNewNinjaRule(name, action['action'], description,
|
| - env=env)
|
| + env=env, rule=action)
|
|
|
| - inputs = [self.GypPathToNinja(i, env) for i in action['inputs']]
|
| + if self.flavor == 'win':
|
| + inputs = [self.msvs_settings.ConvertVSMacros(i)
|
| + for i in action['inputs']]
|
| + else:
|
| + inputs = actions['inputs']
|
| + inputs = [self.GypPathToNinja(i, env) for i in inputs]
|
| if int(action.get('process_outputs_as_sources', False)):
|
| extra_sources += action['outputs']
|
| if int(action.get('process_outputs_as_mac_bundle_resources', False)):
|
| extra_mac_bundle_resources += action['outputs']
|
| - outputs = [self.GypPathToNinja(o, env) for o in action['outputs']]
|
| + if self.flavor == 'win':
|
| + outputs = [self.msvs_settings.ConvertVSMacros(i)
|
| + for i in action['outputs']]
|
| + else:
|
| + outputs = action['outputs']
|
| + outputs = [self.GypPathToNinja(o, env) for o in outputs]
|
|
|
| # Then write out an edge using the rule.
|
| self.ninja.build(outputs, rule_name, inputs,
|
| @@ -464,7 +480,7 @@ class NinjaWriter:
|
| 'RULE',
|
| rule.get('message', None),
|
| ('%s ' + generator_default_variables['RULE_INPUT_PATH']) % name)
|
| - rule_name = self.WriteNewNinjaRule(name, args, description)
|
| + rule_name = self.WriteNewNinjaRule(name, args, description, rule=rule)
|
|
|
| # TODO: if the command references the outputs directly, we should
|
| # simplify it to just use $out.
|
| @@ -479,6 +495,12 @@ class NinjaWriter:
|
| if ('${%s}' % var) in argument:
|
| needed_variables.add(var)
|
|
|
| + is_cygwin = gyp.msvs_emulation.IsRuleRunUnderCygwin(rule)
|
| + def cygwin_munge(path):
|
| + if is_cygwin:
|
| + return path.replace('\\', '/')
|
| + return path
|
| +
|
| # For each source file, write an edge that generates all the outputs.
|
| for source in rule.get('rule_sources', []):
|
| dirname, basename = os.path.split(source)
|
| @@ -498,23 +520,27 @@ class NinjaWriter:
|
| extra_bindings = []
|
| for var in needed_variables:
|
| if var == 'root':
|
| - extra_bindings.append(('root', root))
|
| + extra_bindings.append(('root', cygwin_munge(root)))
|
| elif var == 'dirname':
|
| - extra_bindings.append(('dirname', dirname))
|
| + extra_bindings.append(('dirname', cygwin_munge(dirname)))
|
| elif var == 'source':
|
| # '$source' is a parameter to the rule action, which means
|
| # it shouldn't be converted to a Ninja path. But we don't
|
| # want $!PRODUCT_DIR in there either.
|
| source_expanded = self.ExpandSpecial(source, self.base_to_build)
|
| - extra_bindings.append(('source', source_expanded))
|
| + extra_bindings.append(('source', cygwin_munge(source_expanded)))
|
| elif var == 'ext':
|
| - extra_bindings.append(('ext', ext))
|
| + extra_bindings.append(('ext', cygwin_munge(ext)))
|
| elif var == 'name':
|
| extra_bindings.append(('name', basename))
|
| else:
|
| assert var == None, repr(var)
|
|
|
| - inputs = map(self.GypPathToNinja, rule.get('inputs', []))
|
| + inputs = []
|
| + for input in rule.get('inputs', []):
|
| + inputs.append(self.ExpandRuleVariables(input, root, dirname, source,
|
| + ext, basename))
|
| + inputs = map(self.GypPathToNinja, inputs)
|
| outputs = map(self.GypPathToNinja, outputs)
|
| self.ninja.build(outputs, rule_name, self.GypPathToNinja(source),
|
| implicit=inputs,
|
| @@ -574,7 +600,7 @@ class NinjaWriter:
|
| ('env', env)])
|
| bundle_depends.append(out)
|
|
|
| - def WriteSources(self, config_name, config, sources, predepends,
|
| + def WriteSources(self, config_name, config, sources, predepends, spec,
|
| precompiled_header):
|
| """Write build rules to compile all of |sources|."""
|
| if self.toolset == 'target':
|
| @@ -583,6 +609,7 @@ class NinjaWriter:
|
| self.ninja.variable('cxx', '$cxx_target')
|
| self.ninja.variable('ld', '$ld_target')
|
|
|
| + extra_defines = []
|
| if self.flavor == 'mac':
|
| cflags = self.xcode_settings.GetCflags(config_name)
|
| cflags_c = self.xcode_settings.GetCflagsC(config_name)
|
| @@ -591,17 +618,32 @@ class NinjaWriter:
|
| self.xcode_settings.GetCflagsObjC(config_name)
|
| cflags_objcc = ['$cflags_cc'] + \
|
| self.xcode_settings.GetCflagsObjCC(config_name)
|
| + elif self.flavor == 'win':
|
| + cflags = self.msvs_settings.GetCflags(config_name)
|
| + cflags_c = self.msvs_settings.GetCflagsC(config_name)
|
| + cflags_cc = self.msvs_settings.GetCflagsCC(config_name)
|
| + extra_defines = self.msvs_settings.GetComputedDefines(config_name)
|
| + self.msvs_settings.HandleIdlFiles(
|
| + self.ninja, spec, sources, config_name,
|
| + self.GypPathToNinja,
|
| + self.GypPathToUniqueOutput,
|
| + self.ExpandRuleVariables)
|
| else:
|
| cflags = config.get('cflags', [])
|
| cflags_c = config.get('cflags_c', [])
|
| cflags_cc = config.get('cflags_cc', [])
|
|
|
| + defines = config.get('defines', []) + extra_defines
|
| self.WriteVariableList('defines',
|
| [QuoteShellArgument(ninja_syntax.escape('-D' + d), self.flavor)
|
| - for d in config.get('defines', [])])
|
| + for d in defines])
|
| + include_dirs = config.get('include_dirs', [])
|
| + if self.flavor == 'win':
|
| + include_dirs = self.msvs_settings.AdjustIncludeDirs(include_dirs,
|
| + config_name)
|
| self.WriteVariableList('includes',
|
| - ['-I' + self.GypPathToNinja(i)
|
| - for i in config.get('include_dirs', [])])
|
| + [QuoteShellArgument('-I' + self.GypPathToNinja(i), self.flavor)
|
| + for i in include_dirs])
|
|
|
| pch_commands = precompiled_header.GetGchBuildCommands()
|
| if self.flavor == 'mac':
|
| @@ -636,7 +678,6 @@ class NinjaWriter:
|
| elif self.flavor == 'mac' and ext == 'mm':
|
| command = 'objcxx'
|
| else:
|
| - # TODO: should we assert here on unexpected extensions?
|
| continue
|
| input = self.GypPathToNinja(source)
|
| output = self.GypPathToUniqueOutput(filename + self.obj_ext)
|
| @@ -711,6 +752,14 @@ class NinjaWriter:
|
| ldflags = self.xcode_settings.GetLdflags(config_name,
|
| self.ExpandSpecial(generator_default_variables['PRODUCT_DIR']),
|
| self.GypPathToNinja)
|
| + elif self.flavor == 'win':
|
| + ldflags = self.msvs_settings.GetLdflags(config_name, spec,
|
| + self.ExpandSpecial(generator_default_variables['PRODUCT_DIR']),
|
| + self.GypPathToNinja)
|
| + libflags = self.msvs_settings.GetLibFlags(config_name, spec)
|
| + self.WriteVariableList('libflags',
|
| + gyp.common.uniquer(map(self.ExpandSpecial,
|
| + libflags)))
|
| else:
|
| ldflags = config.get('ldflags', [])
|
| self.WriteVariableList('ldflags',
|
| @@ -721,6 +770,8 @@ class NinjaWriter:
|
| spec.get('libraries', [])))
|
| if self.flavor == 'mac':
|
| libraries = self.xcode_settings.AdjustLibraries(libraries)
|
| + elif self.flavor == 'win':
|
| + libraries = self.msvs_settings.AdjustLibraries(libraries)
|
| self.WriteVariableList('libs', libraries)
|
|
|
| self.target.binary = output
|
| @@ -731,8 +782,8 @@ class NinjaWriter:
|
| import_lib = output + '.lib'
|
| extra_bindings.append(('dll', output))
|
| extra_bindings.append(('implib', import_lib))
|
| - self.target.binary = import_lib
|
| output = [output, import_lib]
|
| + self.target.binary = import_lib
|
|
|
| self.ninja.build(output, command, link_deps,
|
| implicit=list(implicit_deps),
|
| @@ -934,7 +985,7 @@ class NinjaWriter:
|
| values = []
|
| self.ninja.variable(var, ' '.join(values))
|
|
|
| - def WriteNewNinjaRule(self, name, args, description, env={}):
|
| + def WriteNewNinjaRule(self, name, args, description, env={}, rule=None):
|
| """Write out a new ninja "rule" statement for a given command.
|
|
|
| Returns the name of the new rule."""
|
| @@ -950,19 +1001,26 @@ class NinjaWriter:
|
|
|
| args = args[:]
|
|
|
| + is_cygwin = rule and int(rule.get('msvs_cygwin_shell', 1))
|
| +
|
| # gyp dictates that commands are run from the base directory.
|
| # cd into the directory before running, and adjust paths in
|
| # the arguments to point to the proper locations.
|
| if self.flavor == 'win':
|
| - cd = 'cmd /s /c "cd %s && ' % self.build_to_base
|
| + cd = 'cmd /s /c "'
|
| + if not is_cygwin:
|
| + cd += 'cd %s && ' % self.build_to_base
|
| else:
|
| cd = 'cd %s; ' % self.build_to_base
|
| args = [self.ExpandSpecial(arg, self.base_to_build) for arg in args]
|
| env = self.ComputeExportEnvString(env)
|
| if self.flavor == 'win':
|
| - # TODO(scottmg): Respect msvs_cygwin setting here.
|
| - # If there's no command, fake one to match the dangling |&&| above.
|
| - command = gyp.msvs_emulation.EncodeCmdExeList(args) or 'cmd /c'
|
| + if is_cygwin:
|
| + command = gyp.msvs_emulation.BuildCygwinBashCommandLine(
|
| + self.msvs_settings, rule, args, self.build_to_base, self.config_name)
|
| + else:
|
| + # If there's no command, fake one to match the dangling |&&| above.
|
| + command = self.msvs_settings.EncodeCmdExeList(args) or 'cmd /c'
|
| else:
|
| command = gyp.common.EncodePOSIXShellList(args)
|
| if env:
|
| @@ -1011,6 +1069,21 @@ def CalculateVariables(default_variables, params):
|
| default_variables['STATIC_LIB_SUFFIX'] = '.lib'
|
| default_variables['SHARED_LIB_PREFIX'] = ''
|
| default_variables['SHARED_LIB_SUFFIX'] = '.dll'
|
| + generator_flags = params.get('generator_flags', {})
|
| + msvs_version = gyp.msvs_emulation.GetVSVersion(generator_flags)
|
| +
|
| + # Set a variable so conditions can be based on msvs_version.
|
| + default_variables['MSVS_VERSION'] = msvs_version.ShortName()
|
| +
|
| + # To determine processor word size on Windows, in addition to checking
|
| + # PROCESSOR_ARCHITECTURE (which reflects the word size of the current
|
| + # process), it is also necessary to check PROCESSOR_ARCHITEW6432 (which
|
| + # contains the actual word size of the system when running thru WOW64).
|
| + if ('64' in os.environ.get('PROCESSOR_ARCHITECTURE', '') or
|
| + '64' in os.environ.get('PROCESSOR_ARCHITEW6432', '')):
|
| + default_variables['MSVS_OS_BITS'] = 64
|
| + else:
|
| + default_variables['MSVS_OS_BITS'] = 32
|
| else:
|
| operating_system = flavor
|
| if flavor == 'android':
|
| @@ -1033,6 +1106,7 @@ def OpenOutput(path):
|
|
|
| def GenerateOutputForConfig(target_list, target_dicts, data, params,
|
| config_name):
|
| + print "Generating ninja build files for %s..." % config_name
|
| options = params['options']
|
| flavor = gyp.common.GetFlavor(params)
|
| generator_flags = params.get('generator_flags', {})
|
| @@ -1042,16 +1116,17 @@ def GenerateOutputForConfig(target_list, target_dicts, data, params,
|
| build_dir = os.path.join(generator_flags.get('output_dir', 'out'),
|
| config_name)
|
|
|
| + top_build_dir = os.path.join(options.toplevel_dir, build_dir)
|
| master_ninja = ninja_syntax.Writer(
|
| - OpenOutput(os.path.join(options.toplevel_dir, build_dir, 'build.ninja')),
|
| + OpenOutput(os.path.join(top_build_dir, 'build.ninja')),
|
| width=120)
|
|
|
| # Put build-time support tools in out/{config_name}.
|
| - gyp.common.CopyTool(flavor, os.path.join(options.toplevel_dir, build_dir))
|
| + gyp.common.CopyTool(flavor, top_build_dir)
|
|
|
| # Grab make settings for CC/CXX.
|
| if flavor == 'win':
|
| - cc = cxx = 'cl'
|
| + cc = cxx = gyp.msvs_emulation.GetCLPath(generator_flags)
|
| else:
|
| cc, cxx = 'gcc', 'g++'
|
| build_file, _, _ = gyp.common.ParseQualifiedTarget(target_list[0])
|
| @@ -1068,7 +1143,9 @@ def GenerateOutputForConfig(target_list, target_dicts, data, params,
|
| master_ninja.variable('cc', os.environ.get('CC', cc))
|
| master_ninja.variable('cxx', os.environ.get('CXX', cxx))
|
| if flavor == 'win':
|
| - master_ninja.variable('ld', 'link')
|
| + master_ninja.variable('ld', gyp.msvs_emulation.GetLinkPath(generator_flags))
|
| + master_ninja.variable('idl',
|
| + gyp.msvs_emulation.GetMidlPath(generator_flags))
|
| else:
|
| master_ninja.variable('ld', flock + ' linker.lock $cxx')
|
|
|
| @@ -1076,7 +1153,8 @@ def GenerateOutputForConfig(target_list, target_dicts, data, params,
|
| master_ninja.variable('cc_target', os.environ.get('CC_target', '$cc'))
|
| master_ninja.variable('cxx_target', os.environ.get('CXX_target', '$cxx'))
|
| if flavor == 'win':
|
| - master_ninja.variable('ld_target', 'link')
|
| + master_ninja.variable('ld_target', gyp.msvs_emulation.GetLinkPath(
|
| + generator_flags))
|
| else:
|
| master_ninja.variable('ld_target', flock + ' linker.lock $cxx_target')
|
|
|
| @@ -1103,19 +1181,29 @@ def GenerateOutputForConfig(target_list, target_dicts, data, params,
|
| master_ninja.rule(
|
| 'cc',
|
| description='CC $out',
|
| - command=('cmd /c $cc /nologo /showIncludes '
|
| - '$defines $includes $cflags $cflags_c '
|
| - '$cflags_pch_c /c $in /Fo$out '
|
| - '| ninja-deplist-helper -f cl -o $out.dl'),
|
| - deplist='$out.dl')
|
| + command=('cmd /s /c "$cc /nologo /showIncludes '
|
| + '@$out.rsp '
|
| + '$cflags_pch_c /c $in /Fo$out /Fd$out.pdb '
|
| + '| ninja-deplist-helper -d .ninja_depdb -q -f cl -o $in"'),
|
| + depdb='$in',
|
| + rspfile='$out.rsp',
|
| + rspfile_content='$defines $includes $cflags $cflags_c')
|
| master_ninja.rule(
|
| 'cxx',
|
| description='CXX $out',
|
| - command=('cmd /c $cxx /nologo /showIncludes '
|
| - '$defines $includes $cflags $cflags_cc '
|
| - '$cflags_pch_cc /c $in /Fo$out '
|
| - '| ninja-deplist-helper -f cl -o $out.dl'),
|
| - deplist='$out.dl')
|
| + command=('cmd /s /c "$cxx /nologo /showIncludes '
|
| + '@$out.rsp '
|
| + '$cflags_pch_cc /c $in /Fo$out /Fd$out.pdb '
|
| + '| ninja-deplist-helper -d .ninja_depdb -q -f cl -o $in"'),
|
| + depdb='$in',
|
| + rspfile='$out.rsp',
|
| + rspfile_content='$defines $includes $cflags $cflags_cc')
|
| + master_ninja.rule(
|
| + 'idl',
|
| + description='IDL $outdir',
|
| + command=('python gyp-win-tool quiet-midl $outdir '
|
| + '$tlb $h $dlldata $iid $proxy $in '
|
| + '$idlflags'))
|
|
|
| if flavor != 'mac' and flavor != 'win':
|
| master_ninja.rule(
|
| @@ -1141,19 +1229,29 @@ def GenerateOutputForConfig(target_list, target_dicts, data, params,
|
| master_ninja.rule(
|
| 'alink',
|
| description='LIB $out',
|
| - command='lib /nologo /OUT:$out $in')
|
| - master_ninja.rule(
|
| - 'solink',
|
| - description='LINK(DLL) $dll',
|
| - command=('$ld /nologo /IMPLIB:$implib /DLL $ldflags /OUT:$dll $in $libs'))
|
| - master_ninja.rule(
|
| - 'solink_module',
|
| - description='LINK(DLL) $dll',
|
| - command=('$ld /nologo /IMPLIB:$implib /DLL $ldflags /OUT:$dll $in $libs'))
|
| + command='lib /nologo /ignore:4221 $libflags /OUT:$out @$out.rsp',
|
| + rspfile='$out.rsp',
|
| + rspfile_content='$in')
|
| + # TODO(scottmg): The lib is the main output, rather than the dll. link
|
| + # doesn't create the import lib unless there's exports from the dll,
|
| + # however. This means the dependencies are wrong and the dll will always
|
| + # relink if it has no exports. We could touch a dummy file, but we don't
|
| + # want to do && touch, because that would mean we'd have to prepend cmd,
|
| + # which would impose a short line length on the link command line. That
|
| + # can be resolved by adding support for @rsp files, but neither ninja or
|
| + # gyp takes care of that right now. So, currently, we force the export
|
| + # of the entry point, and then ignore the warning that we're doing so,
|
| + # which causes the lib to always get generated.
|
| + dlldesc = 'LINK(DLL) $dll and $implib'
|
| + dllcmd = ('$ld /nologo /IMPLIB:$implib /DLL $ldflags /OUT:$dll '
|
| + '/PDB:$dll.pdb $libs @$dll.rsp')
|
| + master_ninja.rule('solink', description=dlldesc, command=dllcmd,
|
| + rspfile='$dll.rsp', rspfile_content='$in')
|
| + master_ninja.rule('solink_module', description=dlldesc, command=dllcmd)
|
| master_ninja.rule(
|
| 'link',
|
| description='LINK $out',
|
| - command=('$ld /nologo $ldflags /OUT:$out $in $libs'))
|
| + command=('$ld /nologo $ldflags /OUT:$out /PDB:$out.pdb $in $libs'))
|
| else:
|
| master_ninja.rule(
|
| 'objc',
|
| @@ -1212,8 +1310,7 @@ def GenerateOutputForConfig(target_list, target_dicts, data, params,
|
| master_ninja.rule(
|
| 'copy',
|
| description='COPY $in $out',
|
| - command='cmd /c mklink /h $out $in >nul || mklink /h /j $out $in >nul || '
|
| - 'python gyp-win-tool recursive-mirror $in $out')
|
| + command='python gyp-win-tool recursive-mirror $in $out')
|
| else:
|
| master_ninja.rule(
|
| 'stamp',
|
| @@ -1256,11 +1353,9 @@ def GenerateOutputForConfig(target_list, target_dicts, data, params,
|
| obj += '.' + toolset
|
| output_file = os.path.join(obj, base_path, name + '.ninja')
|
|
|
| - abs_build_dir=os.path.abspath(os.path.join(options.toplevel_dir, build_dir))
|
| + abs_build_dir=os.path.abspath(top_build_dir)
|
| writer = NinjaWriter(target_outputs, base_path, build_dir,
|
| - OpenOutput(os.path.join(options.toplevel_dir,
|
| - build_dir,
|
| - output_file)),
|
| + OpenOutput(os.path.join(top_build_dir, output_file)),
|
| flavor, abs_build_dir=abs_build_dir)
|
| master_ninja.subninja(output_file)
|
|
|
|
|