Support specifying target and host platform in gen.py

This is a first step towards a support for cross-compiling GN to
other platforms other than the host.

Change-Id: I895d2951ce01f00541de59e206e55bfc4e49abb9
Reviewed-on: https://gn-review.googlesource.com/2661
Reviewed-by: Brett Wilson <brettw@chromium.org>
Commit-Queue: Petr Hosek <phosek@google.com>
diff --git a/build/gen.py b/build/gen.py
index e333652..76f88e8 100755
--- a/build/gen.py
+++ b/build/gen.py
@@ -21,17 +21,68 @@
 REPO_ROOT = os.path.dirname(SCRIPT_DIR)
 GN_ROOT = os.path.join(REPO_ROOT, 'tools', 'gn')
 
-is_win = sys.platform.startswith('win')
-is_linux = sys.platform.startswith('linux')
-is_mac = sys.platform.startswith('darwin')
-is_aix = sys.platform.startswith('aix')
-is_posix = is_linux or is_mac or is_aix
+
+class Platform(object):
+  """Represents a host/target platform."""
+  def __init__(self, platform):
+    self._platform = platform
+    if self._platform is not None:
+      return
+    self._platform = sys.platform
+    if self._platform.startswith('linux'):
+      self._platform = 'linux'
+    elif self._platform.startswith('darwin'):
+      self._platform = 'darwin'
+    elif self._platform.startswith('mingw'):
+      self._platform = 'mingw'
+    elif self._platform.startswith('win'):
+      self._platform = 'msvc'
+    elif self._platform.startswith('aix'):
+      self._platform = 'aix'
+    elif self._platform.startswith('fuchsia'):
+      self._platform = 'fuchsia'
+
+  @staticmethod
+  def known_platforms():
+    return ['linux', 'darwin', 'msvc', 'aix', 'fuchsia']
+
+  def platform(self):
+    return self._platform
+
+  def is_linux(self):
+    return self._platform == 'linux'
+
+  def is_mingw(self):
+    return self._platform == 'mingw'
+
+  def is_msvc(self):
+    return self._platform == 'msvc'
+
+  def is_windows(self):
+    return self.is_mingw() or self.is_msvc()
+
+  def is_darwin(self):
+    return self._platform == 'darwin'
+
+  def is_aix(self):
+    return self._platform == 'aix'
+
+  def is_posix(self):
+    return self._platform in ['linux', 'darwin', 'aix']
 
 
 def main(argv):
   parser = optparse.OptionParser(description=sys.modules[__name__].__doc__)
   parser.add_option('-d', '--debug', action='store_true',
                     help='Do a debug build. Defaults to release build.')
+  parser.add_option('--platform',
+                    help='target platform (' +
+                         '/'.join(Platform.known_platforms()) + ')',
+                    choices=Platform.known_platforms())
+  parser.add_option('--host',
+                    help='host platform (' +
+                         '/'.join(Platform.known_platforms()) + ')',
+                    choices=Platform.known_platforms())
   parser.add_option('--use-lto', action='store_true',
                     help='Enable the use of LTO')
   parser.add_option('--use-icf', action='store_true',
@@ -47,23 +98,31 @@
   if args:
     parser.error('Unrecognized command line arguments: %s.' % ', '.join(args))
 
+  platform = Platform(options.platform)
+  if options.host:
+    host = Platform(options.host)
+  else:
+    host = platform
+
   linux_sysroot = None
-  if is_linux and not options.no_sysroot:
+  if platform.is_linux() and not options.no_sysroot:
     linux_sysroot = UpdateLinuxSysroot()
 
   out_dir = options.out_path or os.path.join(REPO_ROOT, 'out')
   if not os.path.isdir(out_dir):
     os.makedirs(out_dir)
   if not options.no_last_commit_position:
-    GenerateLastCommitPosition(os.path.join(out_dir, 'last_commit_position.h'))
-  WriteGNNinja(os.path.join(out_dir, 'build.ninja'), options, linux_sysroot)
+    GenerateLastCommitPosition(host,
+                               os.path.join(out_dir, 'last_commit_position.h'))
+  WriteGNNinja(os.path.join(out_dir, 'build.ninja'), platform, host, options,
+               linux_sysroot)
   return 0
 
 
-def GenerateLastCommitPosition(header):
+def GenerateLastCommitPosition(host, header):
   ROOT_TAG = 'initial-commit'
   describe_output = subprocess.check_output(
-      ['git', 'describe', 'HEAD', '--match', ROOT_TAG], shell=is_win,
+      ['git', 'describe', 'HEAD', '--match', ROOT_TAG], shell=host.is_windows(),
       cwd=REPO_ROOT)
   mo = re.match(ROOT_TAG + '-(\d+)-g([0-9a-f]+)', describe_output)
   if not mo:
@@ -136,7 +195,7 @@
 
 
 def WriteGenericNinja(path, static_libraries, executables,
-                      cc, cxx, ar, ld, options,
+                      cc, cxx, ar, ld, platform, host, options,
                       cflags=[], cflags_cc=[], ldflags=[], libflags=[],
                       include_dirs=[], solibs=[]):
   ninja_header_lines = [
@@ -158,16 +217,16 @@
 
 
   template_filename = os.path.join(SCRIPT_DIR, {
-      'win32': 'build_win.ninja.template',
+      'msvc': 'build_win.ninja.template',
       'darwin': 'build_mac.ninja.template',
-      'linux2': 'build_linux.ninja.template',
-      'aix6': 'build_aix.ninja.template'
-  }[sys.platform])
+      'linux': 'build_linux.ninja.template',
+      'aix': 'build_aix.ninja.template',
+  }[platform.platform()])
 
   with open(template_filename) as f:
     ninja_template = f.read()
 
-  if is_win:
+  if platform.is_windows():
     executable_ext = '.exe'
     library_ext = '.lib'
     object_ext = '.obj'
@@ -239,13 +298,13 @@
             os.path.relpath(template_filename, os.path.dirname(path)) + '\n')
 
 
-def WriteGNNinja(path, options, linux_sysroot):
-  if is_win:
+def WriteGNNinja(path, platform, host, options, linux_sysroot):
+  if platform.is_msvc():
     cc = os.environ.get('CC', 'cl.exe')
     cxx = os.environ.get('CXX', 'cl.exe')
     ld = os.environ.get('LD', 'link.exe')
     ar = os.environ.get('AR', 'lib.exe')
-  elif is_aix:
+  elif platform.is_aix():
     cc = os.environ.get('CC', 'gcc')
     cxx = os.environ.get('CXX', 'g++')
     ld = os.environ.get('LD', 'g++')
@@ -263,7 +322,7 @@
   include_dirs = [REPO_ROOT, os.path.dirname(path)]
   libs = []
 
-  if is_posix:
+  if not platform.is_msvc():
     if options.debug:
       cflags.extend(['-O0', '-g'])
     else:
@@ -275,16 +334,16 @@
       # unused functions and data items.
       cflags.extend(['-fdata-sections', '-ffunction-sections'])
       ldflags.extend(['-fdata-sections', '-ffunction-sections'])
-      if is_mac:
+      if platform.is_darwin():
         ldflags.append('-Wl,-dead_strip')
-      elif not is_aix:
+      elif not platform.is_aix():
         # Garbage collection is done by default on aix.
         ldflags.append('-Wl,--gc-sections')
 
       # Omit all symbol information from the output file.
-      if is_mac:
+      if platform.is_darwin():
         ldflags.append('-Wl,-S')
-      elif is_aix:
+      elif platform.is_aix():
         ldflags.append('-Wl,-s')
       else:
         ldflags.append('-Wl,-strip-all')
@@ -303,7 +362,7 @@
     ])
     cflags_cc.extend(['-std=c++14', '-Wno-c++11-narrowing'])
 
-    if is_linux:
+    if platform.is_linux():
       if linux_sysroot:
         # Use the sid sysroot that UpdateLinuxSysroot() downloads.
         cflags.append('--sysroot=' + linux_sysroot)
@@ -317,11 +376,11 @@
           '-ldl',
           '-lpthread',
       ])
-    elif is_mac:
+    elif platform.is_darwin():
       min_mac_version_flag = '-mmacosx-version-min=10.9'
       cflags.append(min_mac_version_flag)
       ldflags.append(min_mac_version_flag)
-    elif is_aix:
+    elif platform.is_aix():
       cflags_cc.append('-maix64')
       ldflags.extend(['-maix64', '-pthread'])
 
@@ -329,7 +388,7 @@
       cflags.extend(['-flto', '-fwhole-program-vtables'])
       ldflags.extend(['-flto', '-fwhole-program-vtables'])
 
-  elif is_win:
+  elif platform.is_msvc():
     if not options.debug:
       cflags.extend(['/Ox', '/DNDEBUG', '/GL'])
       libflags.extend(['/LTCG'])
@@ -605,7 +664,7 @@
       ], 'tool': 'cxx', 'include_dirs': [], 'libs': []},
   }
 
-  if is_posix:
+  if platform.is_posix():
     static_libraries['base']['sources'].extend([
         'base/files/file_enumerator_posix.cc',
         'base/files/file_posix.cc',
@@ -615,12 +674,12 @@
         'base/strings/string16.cc',
     ])
 
-  if is_linux or is_aix:
+  if platform.is_linux() or platform.is_aix():
     static_libraries['base']['sources'].extend([
         'base/strings/sys_string_conversions_posix.cc',
     ])
 
-  if is_mac:
+  if platform.is_darwin():
     static_libraries['base']['sources'].extend([
         'base/files/file_util_mac.mm',
         'base/mac/bundle_locations.mm',
@@ -635,7 +694,7 @@
         '-framework', 'Security',
     ])
 
-  if is_win:
+  if platform.is_windows():
     static_libraries['base']['sources'].extend([
         'base/files/file_enumerator_win.cc',
         'base/files/file_util_win.cc',
@@ -665,8 +724,8 @@
   executables['gn_unittests']['libs'].extend(static_libraries.keys())
 
   WriteGenericNinja(path, static_libraries, executables, cc, cxx, ar, ld,
-                    options, cflags, cflags_cc, ldflags, libflags, include_dirs,
-                    libs)
+                    platform, host, options, cflags, cflags_cc, ldflags,
+                    libflags, include_dirs, libs)
 
 
 if __name__ == '__main__':