| OLD | NEW |
| (Empty) | |
| 1 #!/usr/bin/python |
| 2 |
| 3 """ |
| 4 TestGyp.py: a testing framework for GYP integration tests. |
| 5 """ |
| 6 |
| 7 import os |
| 8 import shutil |
| 9 import stat |
| 10 import sys |
| 11 |
| 12 import TestCommon |
| 13 from TestCommon import __all__ |
| 14 |
| 15 __all__.extend([ |
| 16 'TestGyp', |
| 17 ]) |
| 18 |
| 19 |
| 20 class TestGypBase(TestCommon.TestCommon): |
| 21 """ |
| 22 Class for controlling end-to-end tests of gyp generators. |
| 23 |
| 24 Instantiating this class will create a temporary directory and |
| 25 arrange for its destruction (via the TestCmd superclass) and |
| 26 copy all of the non-gyptest files in the directory hierarchy of the |
| 27 executing script. |
| 28 |
| 29 The default behavior is to test the 'gyp' or 'gyp.bat' file in the |
| 30 current directory. An alternative may be specified explicitly on |
| 31 instantiation, or by setting the TESTGYP_GYP environment variable. |
| 32 |
| 33 This class should be subclassed for each supported gyp generator |
| 34 (format). Various abstract methods below define calling signatures |
| 35 used by the test scripts to invoke builds on the generated build |
| 36 configuration and to run executables generated by those builds. |
| 37 """ |
| 38 |
| 39 build_tool = None |
| 40 |
| 41 def __init__(self, gyp=None, *args, **kw): |
| 42 self.origin_cwd = os.path.abspath(os.path.dirname(sys.argv[0])) |
| 43 |
| 44 if not gyp: |
| 45 gyp = os.environ.get('TESTGYP_GYP') |
| 46 if not gyp: |
| 47 if sys.platform == 'win32': |
| 48 gyp = 'gyp.bat' |
| 49 else: |
| 50 gyp = 'gyp' |
| 51 self.gyp = os.path.abspath(gyp) |
| 52 |
| 53 self.initialize_build_tool() |
| 54 |
| 55 if not kw.has_key('match'): |
| 56 kw['match'] = TestCommon.match_exact |
| 57 |
| 58 if not kw.has_key('workdir'): |
| 59 # Default behavior: the null string causes TestCmd to create |
| 60 # a temporary directory for us. |
| 61 kw['workdir'] = '' |
| 62 |
| 63 super(TestGypBase, self).__init__(*args, **kw) |
| 64 |
| 65 self.copy_test_configuration(self.origin_cwd, self.workdir) |
| 66 |
| 67 def copy_test_configuration(self, source_dir, dest_dir): |
| 68 """ |
| 69 Copies the test configuration from the specified source_dir |
| 70 (the directory in which the test script lives) to the |
| 71 specified dest_dir (a temporary working directory). |
| 72 |
| 73 This ignores all files and directories that begin with |
| 74 the string 'gyptest', and all '.svn' subdirectories. |
| 75 """ |
| 76 for root, dirs, files in os.walk(source_dir): |
| 77 if '.svn' in dirs: |
| 78 dirs.remove('.svn') |
| 79 dirs = [ d for d in dirs if not d.startswith('gyptest') ] |
| 80 files = [ f for f in files if not f.startswith('gyptest') ] |
| 81 for dirname in dirs: |
| 82 source = os.path.join(root, dirname) |
| 83 destination = source.replace(source_dir, dest_dir) |
| 84 os.mkdir(destination) |
| 85 if sys.platform != 'win32': |
| 86 shutil.copystat(source, destination) |
| 87 for filename in files: |
| 88 source = os.path.join(root, filename) |
| 89 destination = source.replace(source_dir, dest_dir) |
| 90 shutil.copy2(source, destination) |
| 91 |
| 92 def initialize_build_tool(self): |
| 93 """ |
| 94 Initializes the .build_tool attribute to the absolute path of |
| 95 an actual executable on the user's $PATH. |
| 96 """ |
| 97 if self.build_tool and not os.path.isabs(self.build_tool): |
| 98 build_tool = self.where_is(self.build_tool) |
| 99 if build_tool: |
| 100 self.build_tool = build_tool |
| 101 |
| 102 def run_gyp(self, gyp_file, *args, **kw): |
| 103 """ |
| 104 Runs gyp against the specified gyp_file with the specified args. |
| 105 """ |
| 106 # TODO: --depth=. works around Chromium-specific tree climbing. |
| 107 args = ('--depth=.', '--format='+self.format) + args |
| 108 return self.run(program=self.gyp, arguments=args, **kw) |
| 109 |
| 110 # |
| 111 # Abstract methods to be defined by format-specific subclasses. |
| 112 # |
| 113 |
| 114 def build_all(self, gyp_file): |
| 115 """ |
| 116 Runs an "all" build of the configuration generated from the |
| 117 specified gyp_file. |
| 118 """ |
| 119 raise NotImplementeError |
| 120 |
| 121 def build_default(self, gyp_file): |
| 122 """ |
| 123 Runs the default build of the configuration generated from the |
| 124 specified gyp_file. |
| 125 """ |
| 126 raise NotImplementeError |
| 127 |
| 128 def build_target(self, gyp_file, target): |
| 129 """ |
| 130 Runs a build of the specified target against the configuration |
| 131 generated from the specified gyp_file. |
| 132 """ |
| 133 raise NotImplementeError |
| 134 |
| 135 def run_built_executable(self, name, *args, **kw): |
| 136 """ |
| 137 Runs an executable program built from a gyp-generated configuration. |
| 138 |
| 139 The specified name should be independent of any particular generator. |
| 140 Subclasses should find the output executable in the appropriate |
| 141 output build directory, tack on any necessary executable suffix, etc. |
| 142 """ |
| 143 raise NotImplementeError |
| 144 |
| 145 |
| 146 class TestGypMake(TestGypBase): |
| 147 """ |
| 148 Subclass for testing the GYP Make generator. |
| 149 """ |
| 150 format = 'make' |
| 151 build_tool = 'make' |
| 152 def build_all(self, gyp_file): |
| 153 """ |
| 154 Builds the Make 'all' target to build all targets for the Makefiles |
| 155 generated from the specified gyp_file. |
| 156 """ |
| 157 self.run_build(gyp_file, 'all') |
| 158 def build_default(self, gyp_file): |
| 159 """ |
| 160 Runs Make with no additional command-line arguments to get the |
| 161 default build for the Makefiles generated from the specified gyp_file. |
| 162 """ |
| 163 self.run_build(gyp_file) |
| 164 def build_target(self, gyp_file, target): |
| 165 """ |
| 166 Runs a Make build with the specified target on the command line |
| 167 to build just that target using the Makefile generated from the |
| 168 specified gyp_file. |
| 169 """ |
| 170 self.run_build(gyp_file, target) |
| 171 def run_build(self, gyp_file, *args): |
| 172 """ |
| 173 Runs a Make build using the Makefiles generated from the specified |
| 174 gyp_file. |
| 175 """ |
| 176 return self.run(program=self.build_tool, arguments=args) |
| 177 def run_built_executable(self, name, *args, **kw): |
| 178 """ |
| 179 Runs an executable built by Make. |
| 180 """ |
| 181 # TODO: generalize to different configurations. |
| 182 program = self.workpath('out/Debug/' + name) |
| 183 return self.run(program=program, *args, **kw) |
| 184 |
| 185 |
| 186 class TestGypMSVS(TestGypBase): |
| 187 """ |
| 188 Subclass for testing the GYP Visual Studio generator. |
| 189 """ |
| 190 format = 'msvs' |
| 191 build_tool = 'devenv' |
| 192 def build_all(self, gyp_file): |
| 193 """ |
| 194 Runs devenv.exe with no target-specific options to get the "all" |
| 195 build for the Visual Studio configuration generated from the |
| 196 specified gyp_file. |
| 197 |
| 198 (NOTE: This is the same as the default, our generated Visual Studio |
| 199 configuration doesn't create an explicit "all" target.) |
| 200 """ |
| 201 return self.run_build(gyp_file) |
| 202 def build_default(self, gyp_file): |
| 203 """ |
| 204 Runs devenv.exe with no target-specific options to get the default |
| 205 build for the Visual Studio configuration generated from the |
| 206 specified gyp_file. |
| 207 """ |
| 208 return self.run_build(gyp_file) |
| 209 def build_target(self, gyp_file, target): |
| 210 """ |
| 211 Uses the devenv.exe /Project option to build the specified target with |
| 212 the Visual Studio configuration generated from the specified gyp_file. |
| 213 """ |
| 214 return self.run_build(gyp_file, '/Project', target) |
| 215 def initialize_build_tool(self): |
| 216 """ |
| 217 Initializes the Visual Studio .build_tool parameter, searching %PATH% |
| 218 and %PATHEXT% for a devenv.{exe,bat,...} executable, and falling |
| 219 back to a hard-coded default (on the current drive) if necessary. |
| 220 """ |
| 221 build_tool = self.where_is(self.build_tool) |
| 222 if build_tool: |
| 223 self.build_tool = build_tool |
| 224 return |
| 225 # We didn't find 'devenv' on the path. Just hard-code a default, |
| 226 # and revisit this if it becomes important. |
| 227 self.build_tool = os.path.join('\\Program Files', |
| 228 'Microsoft Visual Studio 8', |
| 229 'Common7', |
| 230 'IDE', |
| 231 'devenv.exe') |
| 232 def run_build(self, gyp_file, *args): |
| 233 """ |
| 234 Runs a Visual Studio build using the configuration generated |
| 235 from the specified gyp_file. |
| 236 """ |
| 237 # TODO: generalize to different configurations. |
| 238 args = (gyp_file.replace('.gyp', '.sln'), '/Build', 'Default') + args |
| 239 return self.run(program=self.build_tool, arguments=args) |
| 240 def run_built_executable(self, name, *args, **kw): |
| 241 """ |
| 242 Runs an executable built by Visual Studio. |
| 243 """ |
| 244 # TODO: generalize to different configurations. |
| 245 program = self.workpath('Default/%s.exe' % name) |
| 246 return self.run(program=program, *args, **kw) |
| 247 |
| 248 |
| 249 class TestGypSCons(TestGypBase): |
| 250 """ |
| 251 Subclass for testing the GYP SCons generator. |
| 252 """ |
| 253 format = 'scons' |
| 254 build_tool = 'scons' |
| 255 def build_all(self, gyp_file): |
| 256 """ |
| 257 Builds the scons 'all' target to build all targets for the |
| 258 SCons configuration generated from the specified gyp_file. |
| 259 """ |
| 260 self.run_build(gyp_file, 'all') |
| 261 def build_default(self, gyp_file): |
| 262 """ |
| 263 Runs scons with no additional command-line arguments to get the |
| 264 default build for the SCons configuration generated from the |
| 265 specified gyp_file. |
| 266 """ |
| 267 self.run_build(gyp_file) |
| 268 def build_target(self, gyp_file, target): |
| 269 """ |
| 270 Runs a scons build with the specified target on the command line to |
| 271 build just that target using the SCons configuration generated from |
| 272 the specified gyp_file. |
| 273 """ |
| 274 self.run_build(gyp_file, target) |
| 275 def run_build(self, gyp_file, *args): |
| 276 """ |
| 277 Runs a scons build using the SCons configuration generated from the |
| 278 specified gyp_file. |
| 279 """ |
| 280 return self.run(program=self.build_tool, arguments=args) |
| 281 def run_built_executable(self, name, *args, **kw): |
| 282 """ |
| 283 Runs an executable built by scons. |
| 284 """ |
| 285 # TODO: generalize to different configurations. |
| 286 program = self.workpath('Default/' + name) |
| 287 return self.run(program=program, *args, **kw) |
| 288 |
| 289 |
| 290 class TestGypXcode(TestGypBase): |
| 291 """ |
| 292 Subclass for testing the GYP Xcode generator. |
| 293 """ |
| 294 format = 'xcode' |
| 295 build_tool = 'xcodebuild' |
| 296 def build_all(self, gyp_file): |
| 297 """ |
| 298 Uses the xcodebuild -alltargets option to build all targets for the |
| 299 .xcodeproj generated from the specified gyp_file. |
| 300 """ |
| 301 return self.run_build(gyp_file, '-alltargets') |
| 302 def build_default(self, gyp_file): |
| 303 """ |
| 304 Runs xcodebuild with no target-specific options to get the default |
| 305 build for the .xcodeproj generated from the specified gyp_file. |
| 306 """ |
| 307 return self.run_build(gyp_file) |
| 308 def build_target(self, gyp_file, target): |
| 309 """ |
| 310 Uses the xcodebuild -target option to build the specified target |
| 311 with the .xcodeproj generated from the specified gyp_file. |
| 312 """ |
| 313 return self.run_build(gyp_file, '-target', target) |
| 314 def run_build(self, gyp_file, *args): |
| 315 """ |
| 316 Runs an xcodebuild using the .xcodeproj generated from the specified |
| 317 gyp_file. |
| 318 """ |
| 319 args = ('-project', gyp_file.replace('.gyp', '.xcodeproj')) + args |
| 320 return self.run(program=self.build_tool, arguments=args) |
| 321 def run_built_executable(self, name, *args, **kw): |
| 322 """ |
| 323 Runs an executable built by xcodebuild. |
| 324 """ |
| 325 # TODO: generalize to different configurations. |
| 326 program = self.workpath('build/Default/' + name) |
| 327 return self.run(program=program, *args, **kw) |
| 328 |
| 329 |
| 330 format_class_list = [ |
| 331 TestGypMake, |
| 332 TestGypMSVS, |
| 333 TestGypSCons, |
| 334 TestGypXcode, |
| 335 ] |
| 336 |
| 337 def TestGyp(*args, **kw): |
| 338 """ |
| 339 Returns an appropriate TestGyp* instance for a specified GYP format. |
| 340 """ |
| 341 format = kw.get('format') |
| 342 if not format: |
| 343 format = os.environ.get('TESTGYP_FORMAT') |
| 344 for format_class in format_class_list: |
| 345 if format == format_class.format: |
| 346 return format_class(*args, **kw) |
| 347 raise Exception, "unknown format %r" % format |
| OLD | NEW |