OLD | NEW |
1 # Copyright (c) 2012 Google Inc. All rights reserved. | 1 # Copyright (c) 2012 Google Inc. All rights reserved. |
2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
4 | 4 |
5 """ | 5 """ |
6 This module helps emulate Visual Studio 2008 behavior on top of other | 6 This module helps emulate Visual Studio 2008 behavior on top of other |
7 build systems, primarily ninja. | 7 build systems, primarily ninja. |
8 """ | 8 """ |
9 | 9 |
| 10 import collections |
10 import os | 11 import os |
11 import re | 12 import re |
12 import subprocess | 13 import subprocess |
13 import sys | 14 import sys |
14 | 15 |
15 from gyp.common import OrderedSet | 16 from gyp.common import OrderedSet |
16 import gyp.MSVSUtil | 17 import gyp.MSVSUtil |
17 import gyp.MSVSVersion | 18 import gyp.MSVSVersion |
18 | 19 |
| 20 try: |
| 21 basestring = basestring |
| 22 except NameError: |
| 23 basestring = str |
19 | 24 |
20 windows_quoter_regex = re.compile(r'(\\*)"') | 25 windows_quoter_regex = re.compile(r'(\\*)"') |
21 | 26 |
22 | 27 |
23 def QuoteForRspFile(arg): | 28 def QuoteForRspFile(arg): |
24 """Quote a command line argument so that it appears as one argument when | 29 """Quote a command line argument so that it appears as one argument when |
25 processed via cmd.exe and parsed by CommandLineToArgvW (as is typical for | 30 processed via cmd.exe and parsed by CommandLineToArgvW (as is typical for |
26 Windows programs).""" | 31 Windows programs).""" |
27 # See http://goo.gl/cuFbX and http://goo.gl/dhPnp including the comment | 32 # See http://goo.gl/cuFbX and http://goo.gl/dhPnp including the comment |
28 # threads. This is actually the quoting rules for CommandLineToArgvW, not | 33 # threads. This is actually the quoting rules for CommandLineToArgvW, not |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
73 return default | 78 return default |
74 if not path: | 79 if not path: |
75 return root | 80 return root |
76 return _GenericRetrieve(root.get(path[0]), default, path[1:]) | 81 return _GenericRetrieve(root.get(path[0]), default, path[1:]) |
77 | 82 |
78 | 83 |
79 def _AddPrefix(element, prefix): | 84 def _AddPrefix(element, prefix): |
80 """Add |prefix| to |element| or each subelement if element is iterable.""" | 85 """Add |prefix| to |element| or each subelement if element is iterable.""" |
81 if element is None: | 86 if element is None: |
82 return element | 87 return element |
83 # Note, not Iterable because we don't want to handle strings like that. | 88 if (isinstance(element, collections.Iterable) and |
84 if isinstance(element, list) or isinstance(element, tuple): | 89 not isinstance(element, basestring)): |
85 return [prefix + e for e in element] | 90 return [prefix + e for e in element] |
86 else: | 91 else: |
87 return prefix + element | 92 return prefix + element |
88 | 93 |
89 | 94 |
90 def _DoRemapping(element, map): | 95 def _DoRemapping(element, map): |
91 """If |element| then remap it through |map|. If |element| is iterable then | 96 """If |element| then remap it through |map|. If |element| is iterable then |
92 each item will be remapped. Any elements not found will be removed.""" | 97 each item will be remapped. Any elements not found will be removed.""" |
93 if map is not None and element is not None: | 98 if map is not None and element is not None: |
94 if not callable(map): | 99 if not callable(map): |
95 map = map.get # Assume it's a dict, otherwise a callable to do the remap. | 100 map = map.get # Assume it's a dict, otherwise a callable to do the remap. |
96 if isinstance(element, list) or isinstance(element, tuple): | 101 if (isinstance(element, collections.Iterable) and |
| 102 not isinstance(element, basestring)): |
97 element = filter(None, [map(elem) for elem in element]) | 103 element = filter(None, [map(elem) for elem in element]) |
98 else: | 104 else: |
99 element = map(element) | 105 element = map(element) |
100 return element | 106 return element |
101 | 107 |
102 | 108 |
103 def _AppendOrReturn(append, element): | 109 def _AppendOrReturn(append, element): |
104 """If |append| is None, simply return |element|. If |append| is not None, | 110 """If |append| is None, simply return |element|. If |append| is not None, |
105 then add |element| to it, adding each item in |element| if it's a list or | 111 then add |element| to it, adding each item in |element| if it's a list or |
106 tuple.""" | 112 tuple.""" |
107 if append is not None and element is not None: | 113 if append is not None and element is not None: |
108 if isinstance(element, list) or isinstance(element, tuple): | 114 if (isinstance(element, collections.Iterable) and |
| 115 not isinstance(element, basestring)): |
109 append.extend(element) | 116 append.extend(element) |
110 else: | 117 else: |
111 append.append(element) | 118 append.append(element) |
112 else: | 119 else: |
113 return element | 120 return element |
114 | 121 |
115 | 122 |
116 def _FindDirectXInstallation(): | 123 def _FindDirectXInstallation(): |
117 """Try to find an installation location for the DirectX SDK. Check for the | 124 """Try to find an installation location for the DirectX SDK. Check for the |
118 standard environment variable, and if that doesn't exist, try to find | 125 standard environment variable, and if that doesn't exist, try to find |
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
198 ('msvs_system_include_dirs', list), | 205 ('msvs_system_include_dirs', list), |
199 ('msvs_disabled_warnings', list), | 206 ('msvs_disabled_warnings', list), |
200 ('msvs_precompiled_header', str), | 207 ('msvs_precompiled_header', str), |
201 ('msvs_precompiled_source', str), | 208 ('msvs_precompiled_source', str), |
202 ('msvs_configuration_platform', str), | 209 ('msvs_configuration_platform', str), |
203 ('msvs_target_platform', str), | 210 ('msvs_target_platform', str), |
204 ] | 211 ] |
205 configs = spec['configurations'] | 212 configs = spec['configurations'] |
206 for field, default in supported_fields: | 213 for field, default in supported_fields: |
207 setattr(self, field, {}) | 214 setattr(self, field, {}) |
208 for configname, config in configs.iteritems(): | 215 for configname, config in configs.items(): |
209 getattr(self, field)[configname] = config.get(field, default()) | 216 getattr(self, field)[configname] = config.get(field, default()) |
210 | 217 |
211 self.msvs_cygwin_dirs = spec.get('msvs_cygwin_dirs', ['.']) | 218 self.msvs_cygwin_dirs = spec.get('msvs_cygwin_dirs', ['.']) |
212 | 219 |
213 unsupported_fields = [ | 220 unsupported_fields = [ |
214 'msvs_prebuild', | 221 'msvs_prebuild', |
215 'msvs_postbuild', | 222 'msvs_postbuild', |
216 ] | 223 ] |
217 unsupported = [] | 224 unsupported = [] |
218 for field in unsupported_fields: | 225 for field in unsupported_fields: |
(...skipping 246 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
465 cl('AdditionalOptions', prefix='') | 472 cl('AdditionalOptions', prefix='') |
466 cl('EnableEnhancedInstructionSet', | 473 cl('EnableEnhancedInstructionSet', |
467 map={'1': 'SSE', '2': 'SSE2', '3': 'AVX', '4': 'IA32', '5': 'AVX2'}, | 474 map={'1': 'SSE', '2': 'SSE2', '3': 'AVX', '4': 'IA32', '5': 'AVX2'}, |
468 prefix='/arch:') | 475 prefix='/arch:') |
469 cflags.extend(['/FI' + f for f in self._Setting( | 476 cflags.extend(['/FI' + f for f in self._Setting( |
470 ('VCCLCompilerTool', 'ForcedIncludeFiles'), config, default=[])]) | 477 ('VCCLCompilerTool', 'ForcedIncludeFiles'), config, default=[])]) |
471 if self.vs_version.short_name in ('2013', '2013e', '2015'): | 478 if self.vs_version.short_name in ('2013', '2013e', '2015'): |
472 # New flag required in 2013 to maintain previous PDB behavior. | 479 # New flag required in 2013 to maintain previous PDB behavior. |
473 cflags.append('/FS') | 480 cflags.append('/FS') |
474 # ninja handles parallelism by itself, don't have the compiler do it too. | 481 # ninja handles parallelism by itself, don't have the compiler do it too. |
475 cflags = filter(lambda x: not x.startswith('/MP'), cflags) | 482 cflags = [x for x in cflags if not x.startswith('/MP')] |
476 return cflags | 483 return cflags |
477 | 484 |
478 def _GetPchFlags(self, config, extension): | 485 def _GetPchFlags(self, config, extension): |
479 """Get the flags to be added to the cflags for precompiled header support. | 486 """Get the flags to be added to the cflags for precompiled header support. |
480 """ | 487 """ |
481 config = self._TargetConfig(config) | 488 config = self._TargetConfig(config) |
482 # The PCH is only built once by a particular source file. Usage of PCH must | 489 # The PCH is only built once by a particular source file. Usage of PCH must |
483 # only be for the same language (i.e. C vs. C++), so only include the pch | 490 # only be for the same language (i.e. C vs. C++), so only include the pch |
484 # flags when the language matches. | 491 # flags when the language matches. |
485 if self.msvs_precompiled_header[config]: | 492 if self.msvs_precompiled_header[config]: |
(...skipping 145 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
631 if self.GetArch(config) == 'x86': | 638 if self.GetArch(config) == 'x86': |
632 safeseh_default = 'true' | 639 safeseh_default = 'true' |
633 else: | 640 else: |
634 safeseh_default = None | 641 safeseh_default = None |
635 ld('ImageHasSafeExceptionHandlers', | 642 ld('ImageHasSafeExceptionHandlers', |
636 map={'false': ':NO', 'true': ''}, prefix='/SAFESEH', | 643 map={'false': ':NO', 'true': ''}, prefix='/SAFESEH', |
637 default=safeseh_default) | 644 default=safeseh_default) |
638 | 645 |
639 # If the base address is not specifically controlled, DYNAMICBASE should | 646 # If the base address is not specifically controlled, DYNAMICBASE should |
640 # be on by default. | 647 # be on by default. |
641 base_flags = filter(lambda x: 'DYNAMICBASE' in x or x == '/FIXED', | 648 if not any('DYNAMICBASE' in flag or flag == '/FIXED' for flag in ldflags): |
642 ldflags) | |
643 if not base_flags: | |
644 ldflags.append('/DYNAMICBASE') | 649 ldflags.append('/DYNAMICBASE') |
645 | 650 |
646 # If the NXCOMPAT flag has not been specified, default to on. Despite the | 651 # If the NXCOMPAT flag has not been specified, default to on. Despite the |
647 # documentation that says this only defaults to on when the subsystem is | 652 # documentation that says this only defaults to on when the subsystem is |
648 # Vista or greater (which applies to the linker), the IDE defaults it on | 653 # Vista or greater (which applies to the linker), the IDE defaults it on |
649 # unless it's explicitly off. | 654 # unless it's explicitly off. |
650 if not filter(lambda x: 'NXCOMPAT' in x, ldflags): | 655 if not any('NXCOMPAT' in flag for flag in ldflags): |
651 ldflags.append('/NXCOMPAT') | 656 ldflags.append('/NXCOMPAT') |
652 | 657 |
653 have_def_file = filter(lambda x: x.startswith('/DEF:'), ldflags) | 658 have_def_file = any(flag.startswith('/DEF:') for flag in ldflags) |
654 manifest_flags, intermediate_manifest, manifest_files = \ | 659 manifest_flags, intermediate_manifest, manifest_files = \ |
655 self._GetLdManifestFlags(config, manifest_base_name, gyp_to_build_path, | 660 self._GetLdManifestFlags(config, manifest_base_name, gyp_to_build_path, |
656 is_executable and not have_def_file, build_dir) | 661 is_executable and not have_def_file, build_dir) |
657 ldflags.extend(manifest_flags) | 662 ldflags.extend(manifest_flags) |
658 return ldflags, intermediate_manifest, manifest_files | 663 return ldflags, intermediate_manifest, manifest_files |
659 | 664 |
660 def _GetLdManifestFlags(self, config, name, gyp_to_build_path, | 665 def _GetLdManifestFlags(self, config, name, gyp_to_build_path, |
661 allow_isolation, build_dir): | 666 allow_isolation, build_dir): |
662 """Returns a 3-tuple: | 667 """Returns a 3-tuple: |
663 - the set of flags that need to be added to the link to generate | 668 - the set of flags that need to be added to the link to generate |
(...skipping 271 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
935 return vs_version | 940 return vs_version |
936 | 941 |
937 def _GetVsvarsSetupArgs(generator_flags, arch): | 942 def _GetVsvarsSetupArgs(generator_flags, arch): |
938 vs = GetVSVersion(generator_flags) | 943 vs = GetVSVersion(generator_flags) |
939 return vs.SetupScript() | 944 return vs.SetupScript() |
940 | 945 |
941 def ExpandMacros(string, expansions): | 946 def ExpandMacros(string, expansions): |
942 """Expand $(Variable) per expansions dict. See MsvsSettings.GetVSMacroEnv | 947 """Expand $(Variable) per expansions dict. See MsvsSettings.GetVSMacroEnv |
943 for the canonical way to retrieve a suitable dict.""" | 948 for the canonical way to retrieve a suitable dict.""" |
944 if '$' in string: | 949 if '$' in string: |
945 for old, new in expansions.iteritems(): | 950 for old, new in expansions.items(): |
946 assert '$(' not in new, new | 951 assert '$(' not in new, new |
947 string = string.replace(old, new) | 952 string = string.replace(old, new) |
948 return string | 953 return string |
949 | 954 |
950 def _ExtractImportantEnvironment(output_of_set): | 955 def _ExtractImportantEnvironment(output_of_set): |
951 """Extracts environment variables required for the toolchain to run from | 956 """Extracts environment variables required for the toolchain to run from |
952 a textual dump output by the cmd.exe 'set' command.""" | 957 a textual dump output by the cmd.exe 'set' command.""" |
953 envvars_to_save = ( | 958 envvars_to_save = ( |
954 'goma_.*', # TODO(scottmg): This is ugly, but needed for goma. | 959 'goma_.*', # TODO(scottmg): This is ugly, but needed for goma. |
955 'include', | 960 'include', |
(...skipping 27 matching lines...) Expand all Loading... |
983 raise Exception('Environment variable "%s" ' | 988 raise Exception('Environment variable "%s" ' |
984 'required to be set to valid path' % required) | 989 'required to be set to valid path' % required) |
985 return env | 990 return env |
986 | 991 |
987 def _FormatAsEnvironmentBlock(envvar_dict): | 992 def _FormatAsEnvironmentBlock(envvar_dict): |
988 """Format as an 'environment block' directly suitable for CreateProcess. | 993 """Format as an 'environment block' directly suitable for CreateProcess. |
989 Briefly this is a list of key=value\0, terminated by an additional \0. See | 994 Briefly this is a list of key=value\0, terminated by an additional \0. See |
990 CreateProcess documentation for more details.""" | 995 CreateProcess documentation for more details.""" |
991 block = '' | 996 block = '' |
992 nul = '\0' | 997 nul = '\0' |
993 for key, value in envvar_dict.iteritems(): | 998 for key, value in envvar_dict.items(): |
994 block += key + '=' + value + nul | 999 block += key + '=' + value + nul |
995 block += nul | 1000 block += nul |
996 return block | 1001 return block |
997 | 1002 |
998 def _ExtractCLPath(output_of_where): | 1003 def _ExtractCLPath(output_of_where): |
999 """Gets the path to cl.exe based on the output of calling the environment | 1004 """Gets the path to cl.exe based on the output of calling the environment |
1000 setup batch file, followed by the equivalent of `where`.""" | 1005 setup batch file, followed by the equivalent of `where`.""" |
1001 # Take the first line, as that's the first found in the PATH. | 1006 # Take the first line, as that's the first found in the PATH. |
1002 for line in output_of_where.strip().splitlines(): | 1007 for line in output_of_where.strip().splitlines(): |
1003 if line.startswith('LOC:'): | 1008 if line.startswith('LOC:'): |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1038 raise Exception('"%s" failed with error %d' % (args, popen.returncode)) | 1043 raise Exception('"%s" failed with error %d' % (args, popen.returncode)) |
1039 env = _ExtractImportantEnvironment(variables) | 1044 env = _ExtractImportantEnvironment(variables) |
1040 | 1045 |
1041 # Inject system includes from gyp files into INCLUDE. | 1046 # Inject system includes from gyp files into INCLUDE. |
1042 if system_includes: | 1047 if system_includes: |
1043 system_includes = system_includes | OrderedSet( | 1048 system_includes = system_includes | OrderedSet( |
1044 env.get('INCLUDE', '').split(';')) | 1049 env.get('INCLUDE', '').split(';')) |
1045 env['INCLUDE'] = ';'.join(system_includes) | 1050 env['INCLUDE'] = ';'.join(system_includes) |
1046 | 1051 |
1047 env_block = _FormatAsEnvironmentBlock(env) | 1052 env_block = _FormatAsEnvironmentBlock(env) |
1048 f = open_out(os.path.join(toplevel_build_dir, 'environment.' + arch), 'wb') | 1053 f = open_out(os.path.join(toplevel_build_dir, 'environment.' + arch), 'w') |
1049 f.write(env_block) | 1054 f.write(env_block) |
1050 f.close() | 1055 f.close() |
1051 | 1056 |
1052 # Find cl.exe location for this architecture. | 1057 # Find cl.exe location for this architecture. |
1053 args = vs.SetupScript(arch) | 1058 args = vs.SetupScript(arch) |
1054 args.extend(('&&', | 1059 args.extend(('&&', |
1055 'for', '%i', 'in', '(cl.exe)', 'do', '@echo', 'LOC:%~$PATH:i')) | 1060 'for', '%i', 'in', '(cl.exe)', 'do', '@echo', 'LOC:%~$PATH:i')) |
1056 popen = subprocess.Popen(args, shell=True, stdout=subprocess.PIPE) | 1061 popen = subprocess.Popen(args, shell=True, stdout=subprocess.PIPE) |
1057 output, _ = popen.communicate() | 1062 output, _ = popen.communicate() |
1058 cl_paths[arch] = _ExtractCLPath(output) | 1063 cl_paths[arch] = _ExtractCLPath(output) |
1059 return cl_paths | 1064 return cl_paths |
1060 | 1065 |
1061 def VerifyMissingSources(sources, build_dir, generator_flags, gyp_to_ninja): | 1066 def VerifyMissingSources(sources, build_dir, generator_flags, gyp_to_ninja): |
1062 """Emulate behavior of msvs_error_on_missing_sources present in the msvs | 1067 """Emulate behavior of msvs_error_on_missing_sources present in the msvs |
1063 generator: Check that all regular source files, i.e. not created at run time, | 1068 generator: Check that all regular source files, i.e. not created at run time, |
1064 exist on disk. Missing files cause needless recompilation when building via | 1069 exist on disk. Missing files cause needless recompilation when building via |
1065 VS, and we want this check to match for people/bots that build using ninja, | 1070 VS, and we want this check to match for people/bots that build using ninja, |
1066 so they're not surprised when the VS build fails.""" | 1071 so they're not surprised when the VS build fails.""" |
1067 if int(generator_flags.get('msvs_error_on_missing_sources', 0)): | 1072 if int(generator_flags.get('msvs_error_on_missing_sources', 0)): |
1068 no_specials = filter(lambda x: '$' not in x, sources) | 1073 no_specials = filter(lambda x: '$' not in x, sources) |
1069 relative = [os.path.join(build_dir, gyp_to_ninja(s)) for s in no_specials] | 1074 relative = [os.path.join(build_dir, gyp_to_ninja(s)) for s in no_specials] |
1070 missing = filter(lambda x: not os.path.exists(x), relative) | 1075 missing = [x for x in relative if not os.path.exists(x)] |
1071 if missing: | 1076 if missing: |
1072 # They'll look like out\Release\..\..\stuff\things.cc, so normalize the | 1077 # They'll look like out\Release\..\..\stuff\things.cc, so normalize the |
1073 # path for a slightly less crazy looking output. | 1078 # path for a slightly less crazy looking output. |
1074 cleaned_up = [os.path.normpath(x) for x in missing] | 1079 cleaned_up = [os.path.normpath(x) for x in missing] |
1075 raise Exception('Missing input files:\n%s' % '\n'.join(cleaned_up)) | 1080 raise Exception('Missing input files:\n%s' % '\n'.join(cleaned_up)) |
1076 | 1081 |
1077 # Sets some values in default_variables, which are required for many | 1082 # Sets some values in default_variables, which are required for many |
1078 # generators, run on Windows. | 1083 # generators, run on Windows. |
1079 def CalculateCommonVariables(default_variables, params): | 1084 def CalculateCommonVariables(default_variables, params): |
1080 generator_flags = params.get('generator_flags', {}) | 1085 generator_flags = params.get('generator_flags', {}) |
1081 | 1086 |
1082 # Set a variable so conditions can be based on msvs_version. | 1087 # Set a variable so conditions can be based on msvs_version. |
1083 msvs_version = gyp.msvs_emulation.GetVSVersion(generator_flags) | 1088 msvs_version = gyp.msvs_emulation.GetVSVersion(generator_flags) |
1084 default_variables['MSVS_VERSION'] = msvs_version.ShortName() | 1089 default_variables['MSVS_VERSION'] = msvs_version.ShortName() |
1085 | 1090 |
1086 # To determine processor word size on Windows, in addition to checking | 1091 # To determine processor word size on Windows, in addition to checking |
1087 # PROCESSOR_ARCHITECTURE (which reflects the word size of the current | 1092 # PROCESSOR_ARCHITECTURE (which reflects the word size of the current |
1088 # process), it is also necessary to check PROCESSOR_ARCHITEW6432 (which | 1093 # process), it is also necessary to check PROCESSOR_ARCHITEW6432 (which |
1089 # contains the actual word size of the system when running thru WOW64). | 1094 # contains the actual word size of the system when running thru WOW64). |
1090 if ('64' in os.environ.get('PROCESSOR_ARCHITECTURE', '') or | 1095 if ('64' in os.environ.get('PROCESSOR_ARCHITECTURE', '') or |
1091 '64' in os.environ.get('PROCESSOR_ARCHITEW6432', '')): | 1096 '64' in os.environ.get('PROCESSOR_ARCHITEW6432', '')): |
1092 default_variables['MSVS_OS_BITS'] = 64 | 1097 default_variables['MSVS_OS_BITS'] = 64 |
1093 else: | 1098 else: |
1094 default_variables['MSVS_OS_BITS'] = 32 | 1099 default_variables['MSVS_OS_BITS'] = 32 |
OLD | NEW |