#! /usr/bin/env python # Copyright 2009 Google Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # Builds a particlar platform so the user does not have to know platform # specific build commands for every single platform. # TODO(gman): Add help. # TODO(gman): Add cross platform modes like "debug", "opt", "test", "docs" # TODO(gman): Add cross platform switches like "-clean" and "-rebuild". # TODO(gman): Add cross platform options like "-verbose". # TODO(gman): Add cross platform options like "-presubmit", "-selenium", # "-unit_tests" import os import os.path import sys import re import subprocess import platform sys.path.append('build') import is_admin from optparse import OptionParser class GypBuilder(object): """A class to help build gyp projects in a cross platform way""" class Builder(object): """Base Class for building.""" def __init__(self, builder): self.builder = builder def Log(self, *args): """Prints something if verbose is true.""" self.builder.Log(args) def Execute(self, args): """Executes an external program if execute is true.""" self.builder.Execute(args) def Dopresubmit(self, targets, options): """Builds and runs both the unit tests and selenium.""" self.Dounit_tests(targets, options) self.Doselenium(targets, options) def Doselenium(self, targets, options): """Builds and runs the selenium tests.""" print "selenium not yet implemented." def Dounit_tests(self, targets, options): """Builds and runs the unit tests.""" print "unit_tests not yet implemented." def CleanTargets(self, targets, options): """Cleans the targets.""" print "clean not implemented for this platform." class OSXBuilder(Builder): """Class for building on OSX.""" def __init__(self, builder): GypBuilder.Builder.__init__(self, builder) def GetSolutionPath(self): """Gets the solution path.""" return '%s.xcodeproj' % GypBuilder.base_name def CleanTargets(self, targets, options): """Cleans the specifed targets.""" solution = self.GetSolutionPath() self.Execute(['xcodebuild', '-project', solution, 'clean']) def Dobuild(self, targets, options): """Builds the specifed targets.""" solution = self.GetSolutionPath() self.Execute(['xcodebuild', '-project', solution]) class WinBuilder(Builder): """Class for building on Windows.""" def __init__(self, builder): GypBuilder.Builder.__init__(self, builder) def GetSolutionPath(self): """Gets the solution path.""" return os.path.abspath('%s.sln' % GypBuilder.base_name) def CheckVisualStudioVersionVsSolution(self, solution): """Checks the solution matches the cl version.""" f = open(solution, "r") line = f.readline() f.close() m = re.search(r'Format Version (\d+)\.', line) if m: solution_version = int(m.group(1)) else: print "FAILURE: Unknown solution version in %s" % solution sys.exit(1) output = subprocess.Popen(['cl.exe'], stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()[1] m = re.search(r'Compiler Version (\d+)\.', output) if m: compiler_version = int(m.group(1)) else: print "FAILURE: Unknown cl.exe version." sys.exit(1) # Compiler Solution # Visual Studio .NET 2005 14 9 # Visual Studio .NET 2008 15 10 # Visual Studio .NET 2010 ?? ?? if (compiler_version - 14) > (solution_version - 9): vs_map = { 14: '2005', 15: '2008', 16: '2010', } sln_map = { 9: '2005', 10: '2008', 11: '2010', } vs_version = vs_map[compiler_version] print ("ERROR: solution (%s) version does not match " "Visual Studio version (%s)" % (sln_map[solution_version], vs_version)) print "You should 'set GYP_MSVS_VERSION=auto'" print "and run 'gclient runhooks --force'" sys.exit(1) def CleanTargets(self, targets, options): """Cleans the targets.""" solution = self.GetSolutionPath() self.Execute(['devenv.com', solution, '/clean', options.version]) def Dobuild(self, targets, options): """Builds the specifed targets.""" solution = self.GetSolutionPath() if not is_admin.IsAdmin(): print ("WARNING: selenium_ie will not run unless you run as admin " "or turn off UAC.\nAfter switching to admin run " "'gclient runhooks --force'") self.CheckVisualStudioVersionVsSolution(solution) self.Execute(['devenv.com', solution, '/build', options.version]) # TODO(gman): Should I check for devenv and if it does not exist # use msbuild? Msbuild is significantly slower than devenv. #self.Execute(['msbuild', # solution, # '/p:Configuration=%s' % options.version]) class LinuxBuilder(Builder): """Class for building on Linux.""" def __init__(self, builder): GypBuilder.Builder.__init__(self, builder) def GetSolutionPath(self): """Gets the solution path.""" return '%s.Makefile' % GypBuilder.base_name def CleanTargets(self, targets, options): """Cleans the targets.""" print "clean not implemented for this platform." def Dobuild(self, targets, options): """Builds the specifed targets.""" solution = self.GetSolutionPath() self.Execute(['make', '-f', solution]) # Use "o3d" for chrome only build? base_name = "o3d_all" def __init__(self, args): self.execute = True self.verbose = False modes = ["build", "presubmit", "selenium", "unit_tests"] versions = ["Debug", "Release"] parser = OptionParser() parser.add_option( "--list-targets", action="store_true", help="lists all available targets.") parser.add_option( "--no-execute", action="store_true", default=False, help="just prints commands that would get executed.") parser.add_option( "--verbose", action="store_true", help="prints more output.") parser.add_option( "--targets", action="append", help="targets to build separated by commas.") parser.add_option( "--clean", action="store_true", help="cleans the targets.") parser.add_option( "--rebuild", action="store_true", help="cleans, then builds targets") parser.add_option( "--version", choices=versions, default="Debug", help="version to build. Versions are '%s'. Default='Debug' " % "', '".join(versions)) parser.add_option( "--mode", choices=modes, default="build", help="mode to use. Valid modes are '%s'. Default='build' " % "', '".join(modes)) (options, args) = parser.parse_args(args=args) self.verbose = options.verbose self.execute = not options.no_execute if options.list_targets: print "Not yet implemented" sys.exit(0) self.Log("mode:", options.mode) targets = options.targets if targets: # flatten the targets. targets = sum([t.split(",") for t in targets], []) os.chdir("build") # Create a platform specific builder. if os.name == 'nt': builder = self.WinBuilder(self) elif platform.system() == 'Darwin': builder = self.OSXBuilder(self) elif platform.system() == 'Linux': builder = self.LinuxBuilder(self) else: print "ERROR: Unknown platform." sys.exit(1) # clean if asked. if options.clean or options.rebuild: builder.CleanTargets(targets, options) if not options.rebuild: return # call a Do method based on the mode. func = getattr(builder, "Do%s" % options.mode) func(targets, options) def Log(self, *args): """Prints something if verbose is true.""" if self.verbose: print args def Execute(self, args): """Executes an external program if execute is true.""" if self.execute: self.Log(" ".join(args)) if subprocess.call(args) > 0: raise RuntimeError("FAILED: " + " ".join(args)) else: print " ".join(args) def main(args): GypBuilder(args[1:]) if __name__ == "__main__": main(sys.argv)