Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(249)

Side by Side Diff: test/lib/TestGyp.py

Issue 245923003: Fix msvs-ninja OutputDirectory path. (Closed) Base URL: http://gyp.googlecode.com/svn/trunk/
Patch Set: Simplify and correct pathing. Created 6 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « test/ios/gyptest-xcode-ninja.py ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 TestGyp.py: a testing framework for GYP integration tests. 6 TestGyp.py: a testing framework for GYP integration tests.
7 """ 7 """
8 8
9 import collections 9 import collections
10 from contextlib import contextmanager 10 from contextlib import contextmanager
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after
71 The default behavior is to test the 'gyp' or 'gyp.bat' file in the 71 The default behavior is to test the 'gyp' or 'gyp.bat' file in the
72 current directory. An alternative may be specified explicitly on 72 current directory. An alternative may be specified explicitly on
73 instantiation, or by setting the TESTGYP_GYP environment variable. 73 instantiation, or by setting the TESTGYP_GYP environment variable.
74 74
75 This class should be subclassed for each supported gyp generator 75 This class should be subclassed for each supported gyp generator
76 (format). Various abstract methods below define calling signatures 76 (format). Various abstract methods below define calling signatures
77 used by the test scripts to invoke builds on the generated build 77 used by the test scripts to invoke builds on the generated build
78 configuration and to run executables generated by those builds. 78 configuration and to run executables generated by those builds.
79 """ 79 """
80 80
81 formats = []
81 build_tool = None 82 build_tool = None
82 build_tool_list = [] 83 build_tool_list = []
83 84
84 _exe = TestCommon.exe_suffix 85 _exe = TestCommon.exe_suffix
85 _obj = TestCommon.obj_suffix 86 _obj = TestCommon.obj_suffix
86 shobj_ = TestCommon.shobj_prefix 87 shobj_ = TestCommon.shobj_prefix
87 _shobj = TestCommon.shobj_suffix 88 _shobj = TestCommon.shobj_suffix
88 lib_ = TestCommon.lib_prefix 89 lib_ = TestCommon.lib_prefix
89 _lib = TestCommon.lib_suffix 90 _lib = TestCommon.lib_suffix
90 dll_ = TestCommon.dll_prefix 91 dll_ = TestCommon.dll_prefix
(...skipping 15 matching lines...) Expand all
106 if not gyp: 107 if not gyp:
107 gyp = os.environ.get('TESTGYP_GYP') 108 gyp = os.environ.get('TESTGYP_GYP')
108 if not gyp: 109 if not gyp:
109 if sys.platform == 'win32': 110 if sys.platform == 'win32':
110 gyp = 'gyp.bat' 111 gyp = 'gyp.bat'
111 else: 112 else:
112 gyp = 'gyp' 113 gyp = 'gyp'
113 self.gyp = os.path.abspath(gyp) 114 self.gyp = os.path.abspath(gyp)
114 self.no_parallel = False 115 self.no_parallel = False
115 116
117 self.formats = [self.format]
118
116 self.initialize_build_tool() 119 self.initialize_build_tool()
117 120
118 kw.setdefault('match', TestCommon.match_exact) 121 kw.setdefault('match', TestCommon.match_exact)
119 122
120 # Put test output in out/testworkarea by default. 123 # Put test output in out/testworkarea by default.
121 # Use temporary names so there are no collisions. 124 # Use temporary names so there are no collisions.
122 workdir = os.path.join('out', kw.get('workdir', 'testworkarea')) 125 workdir = os.path.join('out', kw.get('workdir', 'testworkarea'))
123 # Create work area if it doesn't already exist. 126 # Create work area if it doesn't already exist.
124 if not os.path.isdir(workdir): 127 if not os.path.isdir(workdir):
125 os.makedirs(workdir) 128 os.makedirs(workdir)
126 129
127 kw['workdir'] = tempfile.mktemp(prefix='testgyp.', dir=workdir) 130 kw['workdir'] = tempfile.mktemp(prefix='testgyp.', dir=workdir)
128 131
129 formats = kw.pop('formats', []) 132 formats = kw.pop('formats', [])
130 133
131 super(TestGypBase, self).__init__(*args, **kw) 134 super(TestGypBase, self).__init__(*args, **kw)
132 135
136 real_format = self.format.split('-')[-1]
133 excluded_formats = set([f for f in formats if f[0] == '!']) 137 excluded_formats = set([f for f in formats if f[0] == '!'])
134 included_formats = set(formats) - excluded_formats 138 included_formats = set(formats) - excluded_formats
135 if ('!'+self.format in excluded_formats or 139 if ('!'+real_format in excluded_formats or
136 included_formats and self.format not in included_formats): 140 included_formats and real_format not in included_formats):
137 msg = 'Invalid test for %r format; skipping test.\n' 141 msg = 'Invalid test for %r format; skipping test.\n'
138 self.skip_test(msg % self.format) 142 self.skip_test(msg % self.format)
139 143
140 self.copy_test_configuration(self.origin_cwd, self.workdir) 144 self.copy_test_configuration(self.origin_cwd, self.workdir)
141 self.set_configuration(None) 145 self.set_configuration(None)
142 146
143 # Set $HOME so that gyp doesn't read the user's actual 147 # Set $HOME so that gyp doesn't read the user's actual
144 # ~/.gyp/include.gypi file, which may contain variables 148 # ~/.gyp/include.gypi file, which may contain variables
145 # and other settings that would change the output. 149 # and other settings that would change the output.
146 os.environ['HOME'] = self.workpath() 150 os.environ['HOME'] = self.workpath()
(...skipping 118 matching lines...) Expand 10 before | Expand all | Expand 10 after
265 Runs gyp against the specified gyp_file with the specified args. 269 Runs gyp against the specified gyp_file with the specified args.
266 """ 270 """
267 271
268 # When running gyp, and comparing its output we use a comparitor 272 # When running gyp, and comparing its output we use a comparitor
269 # that ignores the line numbers that gyp logs in its debug output. 273 # that ignores the line numbers that gyp logs in its debug output.
270 if kw.pop('ignore_line_numbers', False): 274 if kw.pop('ignore_line_numbers', False):
271 kw.setdefault('match', match_modulo_line_numbers) 275 kw.setdefault('match', match_modulo_line_numbers)
272 276
273 # TODO: --depth=. works around Chromium-specific tree climbing. 277 # TODO: --depth=. works around Chromium-specific tree climbing.
274 depth = kw.pop('depth', '.') 278 depth = kw.pop('depth', '.')
275 run_args = ['--depth='+depth, '--format='+self.format, gyp_file] 279 run_args = ['--depth='+depth]
280 run_args.extend(['--format='+f for f in self.formats]);
281 run_args.append(gyp_file)
276 if self.no_parallel: 282 if self.no_parallel:
277 run_args += ['--no-parallel'] 283 run_args += ['--no-parallel']
284 # TODO: if extra_args contains a '--build' flag
285 # we really want that to only apply to the last format (self.format).
278 run_args.extend(self.extra_args) 286 run_args.extend(self.extra_args)
279 run_args.extend(args) 287 run_args.extend(args)
280 return self.run(program=self.gyp, arguments=run_args, **kw) 288 return self.run(program=self.gyp, arguments=run_args, **kw)
281 289
282 def run(self, *args, **kw): 290 def run(self, *args, **kw):
283 """ 291 """
284 Executes a program by calling the superclass .run() method. 292 Executes a program by calling the superclass .run() method.
285 293
286 This exists to provide a common place to filter out keyword 294 This exists to provide a common place to filter out keyword
287 arguments implemented in this layer, without having to update 295 arguments implemented in this layer, without having to update
(...skipping 452 matching lines...) Expand 10 before | Expand all | Expand 10 after
740 748
741 749
742 def ConvertToCygpath(path): 750 def ConvertToCygpath(path):
743 """Convert to cygwin path if we are using cygwin.""" 751 """Convert to cygwin path if we are using cygwin."""
744 if sys.platform == 'cygwin': 752 if sys.platform == 'cygwin':
745 p = subprocess.Popen(['cygpath', path], stdout=subprocess.PIPE) 753 p = subprocess.Popen(['cygpath', path], stdout=subprocess.PIPE)
746 path = p.communicate()[0].strip() 754 path = p.communicate()[0].strip()
747 return path 755 return path
748 756
749 757
758 def FindMSBuildInstallation(msvs_version = 'auto'):
759 """Returns path to MSBuild for msvs_version or latest available.
760
761 Looks in the registry to find install location of MSBuild.
762 MSBuild before v4.0 will not build c++ projects, so only use newer versions.
763 """
764 import _winreg
scottmg 2014/06/02 16:04:49 I don't think this module is available on cygwin p
bungeman-chromium 2014/06/02 17:35:31 I was not aware of that. Done.
765 def get_reg_key(base, key):
766 try:
767 result = _winreg.OpenKey(base, key, 0,
768 _winreg.KEY_READ | _winreg.KEY_WOW64_32KEY)
769 except WindowsError:
770 result = None
771 return result
772
773 def get_reg_key_value(key, value):
774 try:
775 result, _ = _winreg.QueryValueEx(key, value)
776 except WindowsError:
777 result = None
778 return result
779
780 msbuild_basekey = get_reg_key(_winreg.HKEY_LOCAL_MACHINE,
781 'SOFTWARE\Microsoft\MSBuild\ToolsVersions')
782 if not msbuild_basekey:
783 print 'Error: could not find MSBuild base registry entry'
784 return None
785
786 msvs_to_msbuild = {
787 '2013': r'12.0',
788 '2012': r'4.0', #really v4.0.30319 which comes with .NET 4.5
scottmg 2014/06/02 16:04:49 nit; two spaces before #, one after, capital R, en
bungeman-chromium 2014/06/02 17:35:31 Done.
789 '2010': r'4.0'}
790
791 msbuild_key = None
792 if msvs_version in msvs_to_msbuild:
793 msbuild_version = msvs_to_msbuild[msvs_version]
794 msbuild_key = get_reg_key(msbuild_basekey, msbuild_version)
795 if not msbuild_key:
796 print ('Warning: Environment variable GYP_MSVS_VERSION specifies "%s" '
797 'but corresponding MSBuild "%s" was not found.' %
798 (msvs_version, msbuild_version))
799 if not msbuild_key:
800 for msvs_version in sorted(msvs_to_msbuild, reverse=True):
801 msbuild_version = msvs_to_msbuild[msvs_version]
802 msbuild_key = get_reg_key(msbuild_basekey, msbuild_version)
803 if not msbuild_key:
804 print 'Error: could not find MSBuild registry entry'
805 return None
806
807 msbuild_path = get_reg_key_value(msbuild_key, 'MSBuildToolsPath')
808 if not msbuild_path:
809 print 'Error: could not get MSBuild registry entry value'
810 return None
811
812 return os.path.join(msbuild_path, 'MSBuild.exe')
813
814
750 def FindVisualStudioInstallation(): 815 def FindVisualStudioInstallation():
751 """Returns appropriate values for .build_tool and .uses_msbuild fields 816 """Returns appropriate values for .build_tool and .uses_msbuild fields
752 of TestGypBase for Visual Studio. 817 of TestGypBase for Visual Studio.
753 818
754 We use the value specified by GYP_MSVS_VERSION. If not specified, we 819 We use the value specified by GYP_MSVS_VERSION. If not specified, we
755 search %PATH% and %PATHEXT% for a devenv.{exe,bat,...} executable. 820 search %PATH% and %PATHEXT% for a devenv.{exe,bat,...} executable.
756 Failing that, we search for likely deployment paths. 821 Failing that, we search for likely deployment paths.
757 """ 822 """
758 possible_roots = ['%s:\\Program Files%s' % (chr(drive), suffix) 823 possible_roots = ['%s:\\Program Files%s' % (chr(drive), suffix)
759 for drive in range(ord('C'), ord('Z') + 1) 824 for drive in range(ord('C'), ord('Z') + 1)
760 for suffix in ['', ' (x86)']] 825 for suffix in ['', ' (x86)']]
761 possible_paths = { 826 possible_paths = {
762 '2013': r'Microsoft Visual Studio 12.0\Common7\IDE\devenv.com', 827 '2013': r'Microsoft Visual Studio 12.0\Common7\IDE\devenv.com',
763 '2012': r'Microsoft Visual Studio 11.0\Common7\IDE\devenv.com', 828 '2012': r'Microsoft Visual Studio 11.0\Common7\IDE\devenv.com',
764 '2010': r'Microsoft Visual Studio 10.0\Common7\IDE\devenv.com', 829 '2010': r'Microsoft Visual Studio 10.0\Common7\IDE\devenv.com',
765 '2008': r'Microsoft Visual Studio 9.0\Common7\IDE\devenv.com', 830 '2008': r'Microsoft Visual Studio 9.0\Common7\IDE\devenv.com',
766 '2005': r'Microsoft Visual Studio 8\Common7\IDE\devenv.com'} 831 '2005': r'Microsoft Visual Studio 8\Common7\IDE\devenv.com'}
767 832
768 possible_roots = [ConvertToCygpath(r) for r in possible_roots] 833 possible_roots = [ConvertToCygpath(r) for r in possible_roots]
769 834
770 msvs_version = 'auto' 835 msvs_version = 'auto'
771 for flag in (f for f in sys.argv if f.startswith('msvs_version=')): 836 for flag in (f for f in sys.argv if f.startswith('msvs_version=')):
772 msvs_version = flag.split('=')[-1] 837 msvs_version = flag.split('=')[-1]
773 msvs_version = os.environ.get('GYP_MSVS_VERSION', msvs_version) 838 msvs_version = os.environ.get('GYP_MSVS_VERSION', msvs_version)
774 839
775 build_tool = None
776 if msvs_version in possible_paths: 840 if msvs_version in possible_paths:
777 # Check that the path to the specified GYP_MSVS_VERSION exists. 841 # Check that the path to the specified GYP_MSVS_VERSION exists.
778 path = possible_paths[msvs_version] 842 path = possible_paths[msvs_version]
779 for r in possible_roots: 843 for r in possible_roots:
780 bt = os.path.join(r, path) 844 build_tool = os.path.join(r, path)
781 if os.path.exists(bt): 845 if os.path.exists(build_tool):
782 build_tool = bt
783 uses_msbuild = msvs_version >= '2010' 846 uses_msbuild = msvs_version >= '2010'
784 return build_tool, uses_msbuild 847 msbuild_path = FindMSBuildInstallation(msvs_version)
848 return build_tool, uses_msbuild, msbuild_path
785 else: 849 else:
786 print ('Warning: Environment variable GYP_MSVS_VERSION specifies "%s" ' 850 print ('Warning: Environment variable GYP_MSVS_VERSION specifies "%s" '
787 'but corresponding "%s" was not found.' % (msvs_version, path)) 851 'but corresponding "%s" was not found.' % (msvs_version, path))
788 if build_tool:
789 # We found 'devenv' on the path, use that and try to guess the version.
790 for version, path in possible_paths.iteritems():
791 if build_tool.find(path) >= 0:
792 uses_msbuild = version >= '2010'
793 return build_tool, uses_msbuild
794 else:
795 # If not, assume not MSBuild.
796 uses_msbuild = False
797 return build_tool, uses_msbuild
798 # Neither GYP_MSVS_VERSION nor the path help us out. Iterate through 852 # Neither GYP_MSVS_VERSION nor the path help us out. Iterate through
799 # the choices looking for a match. 853 # the choices looking for a match.
800 for version in sorted(possible_paths, reverse=True): 854 for version in sorted(possible_paths, reverse=True):
801 path = possible_paths[version] 855 path = possible_paths[version]
802 for r in possible_roots: 856 for r in possible_roots:
803 bt = os.path.join(r, path) 857 build_tool = os.path.join(r, path)
804 if os.path.exists(bt): 858 if os.path.exists(build_tool):
805 build_tool = bt
806 uses_msbuild = msvs_version >= '2010' 859 uses_msbuild = msvs_version >= '2010'
807 return build_tool, uses_msbuild 860 msbuild_path = FindMSBuildInstallation(msvs_version)
861 return build_tool, uses_msbuild, msbuild_path
808 print 'Error: could not find devenv' 862 print 'Error: could not find devenv'
809 sys.exit(1) 863 sys.exit(1)
810 864
811 class TestGypOnMSToolchain(TestGypBase): 865 class TestGypOnMSToolchain(TestGypBase):
812 """ 866 """
813 Common subclass for testing generators that target the Microsoft Visual 867 Common subclass for testing generators that target the Microsoft Visual
814 Studio toolchain (cl, link, dumpbin, etc.) 868 Studio toolchain (cl, link, dumpbin, etc.)
815 """ 869 """
816 @staticmethod 870 @staticmethod
817 def _ComputeVsvarsPath(devenv_path): 871 def _ComputeVsvarsPath(devenv_path):
818 devenv_dir = os.path.split(devenv_path)[0] 872 devenv_dir = os.path.split(devenv_path)[0]
819 vsvars_path = os.path.join(devenv_path, '../../Tools/vsvars32.bat') 873 vsvars_path = os.path.join(devenv_path, '../../Tools/vsvars32.bat')
820 return vsvars_path 874 return vsvars_path
821 875
822 def initialize_build_tool(self): 876 def initialize_build_tool(self):
823 super(TestGypOnMSToolchain, self).initialize_build_tool() 877 super(TestGypOnMSToolchain, self).initialize_build_tool()
824 if sys.platform in ('win32', 'cygwin'): 878 if sys.platform in ('win32', 'cygwin'):
825 self.devenv_path, self.uses_msbuild = FindVisualStudioInstallation() 879 build_tools = FindVisualStudioInstallation()
880 self.devenv_path, self.uses_msbuild, self.msbuild_path = build_tools
826 self.vsvars_path = TestGypOnMSToolchain._ComputeVsvarsPath( 881 self.vsvars_path = TestGypOnMSToolchain._ComputeVsvarsPath(
827 self.devenv_path) 882 self.devenv_path)
828 883
829 def run_dumpbin(self, *dumpbin_args): 884 def run_dumpbin(self, *dumpbin_args):
830 """Run the dumpbin tool with the specified arguments, and capturing and 885 """Run the dumpbin tool with the specified arguments, and capturing and
831 returning stdout.""" 886 returning stdout."""
832 assert sys.platform in ('win32', 'cygwin') 887 assert sys.platform in ('win32', 'cygwin')
833 cmd = os.environ.get('COMSPEC', 'cmd.exe') 888 cmd = os.environ.get('COMSPEC', 'cmd.exe')
834 arguments = [cmd, '/c', self.vsvars_path, '&&', 'dumpbin'] 889 arguments = [cmd, '/c', self.vsvars_path, '&&', 'dumpbin']
835 arguments.extend(dumpbin_args) 890 arguments.extend(dumpbin_args)
(...skipping 159 matching lines...) Expand 10 before | Expand all | Expand 10 after
995 chdir = kw.get('chdir') 1050 chdir = kw.get('chdir')
996 if chdir: 1051 if chdir:
997 result.append(chdir) 1052 result.append(chdir)
998 result.append(self.configuration_dirname()) 1053 result.append(self.configuration_dirname())
999 if type == self.STATIC_LIB: 1054 if type == self.STATIC_LIB:
1000 result.append('lib') 1055 result.append('lib')
1001 result.append(self.built_file_basename(name, type, **kw)) 1056 result.append(self.built_file_basename(name, type, **kw))
1002 return self.workpath(*result) 1057 return self.workpath(*result)
1003 1058
1004 1059
1060 class TestGypMSVSNinja(TestGypNinja):
1061 """
1062 Subclass for testing the GYP Visual Studio Ninja generator.
1063 """
1064 format = 'msvs-ninja'
1065
1066 def initialize_build_tool(self):
1067 super(TestGypMSVSNinja, self).initialize_build_tool()
1068 # When using '--build', make sure ninja is first in the format list.
1069 self.formats.insert(0, 'ninja')
1070
1071 def build(self, gyp_file, target=None, rebuild=False, clean=False, **kw):
1072 """
1073 Runs a Visual Studio build using the configuration generated
1074 from the specified gyp_file.
1075 """
1076 arguments = kw.get('arguments', [])[:]
1077 if target in (None, self.ALL, self.DEFAULT):
1078 # Note: the Visual Studio generator doesn't add an explicit 'all' target.
1079 # This will build each project. This will work if projects are hermetic,
1080 # but may fail if they are not (a project may run more than once).
1081 # It would be nice to supply an all.metaproj for MSBuild.
1082 arguments.extend([gyp_file.replace('.gyp', '.sln')])
1083 else:
1084 # MSBuild documentation claims that one can specify a sln but then build a
1085 # project target like 'msbuild a.sln /t:proj:target' but this format only
1086 # supports 'Clean', 'Rebuild', and 'Publish' (with none meaning Default).
1087 # This limitation is due to the .sln -> .sln.metaproj conversion.
1088 # The ':' is not special, 'proj:target' is a target in the metaproj.
1089 arguments.extend([target+'.vcxproj'])
1090
1091 if clean:
1092 build = 'Clean'
1093 elif rebuild:
1094 build = 'Rebuild'
1095 else:
1096 build = 'Build'
1097 arguments.extend(['/target:'+build])
1098 configuration = self.configuration_buildname()
1099 config = configuration.split('|')
1100 arguments.extend(['/property:Configuration='+config[0]])
1101 if len(config) > 1:
1102 arguments.extend(['/property:Platform='+config[1]])
1103 arguments.extend(['/property:BuildInParallel=false'])
1104 arguments.extend(['/verbosity:minimal'])
1105
1106 kw['arguments'] = arguments
1107 return self.run(program=self.msbuild_path, **kw)
1108
1109
1005 class TestGypXcode(TestGypBase): 1110 class TestGypXcode(TestGypBase):
1006 """ 1111 """
1007 Subclass for testing the GYP Xcode generator. 1112 Subclass for testing the GYP Xcode generator.
1008 """ 1113 """
1009 format = 'xcode' 1114 format = 'xcode'
1010 build_tool_list = ['xcodebuild'] 1115 build_tool_list = ['xcodebuild']
1011 1116
1012 phase_script_execution = ("\n" 1117 phase_script_execution = ("\n"
1013 "PhaseScriptExecution /\\S+/Script-[0-9A-F]+\\.sh\n" 1118 "PhaseScriptExecution /\\S+/Script-[0-9A-F]+\\.sh\n"
1014 " cd /\\S+\n" 1119 " cd /\\S+\n"
(...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after
1111 result.append(self.built_file_basename(name, type, **kw)) 1216 result.append(self.built_file_basename(name, type, **kw))
1112 return self.workpath(*result) 1217 return self.workpath(*result)
1113 1218
1114 1219
1115 format_class_list = [ 1220 format_class_list = [
1116 TestGypGypd, 1221 TestGypGypd,
1117 TestGypAndroid, 1222 TestGypAndroid,
1118 TestGypCMake, 1223 TestGypCMake,
1119 TestGypMake, 1224 TestGypMake,
1120 TestGypMSVS, 1225 TestGypMSVS,
1226 TestGypMSVSNinja,
1121 TestGypNinja, 1227 TestGypNinja,
1122 TestGypXcode, 1228 TestGypXcode,
1123 ] 1229 ]
1124 1230
1125 def TestGyp(*args, **kw): 1231 def TestGyp(*args, **kw):
1126 """ 1232 """
1127 Returns an appropriate TestGyp* instance for a specified GYP format. 1233 Returns an appropriate TestGyp* instance for a specified GYP format.
1128 """ 1234 """
1129 format = kw.pop('format', os.environ.get('TESTGYP_FORMAT')) 1235 format = kw.pop('format', os.environ.get('TESTGYP_FORMAT'))
1130 for format_class in format_class_list: 1236 for format_class in format_class_list:
1131 if format == format_class.format: 1237 if format == format_class.format:
1132 return format_class(*args, **kw) 1238 return format_class(*args, **kw)
1133 raise Exception, "unknown format %r" % format 1239 raise Exception, "unknown format %r" % format
OLDNEW
« no previous file with comments | « test/ios/gyptest-xcode-ninja.py ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698