#!/usr/bin/python
# Copyright (c) 2011 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

'''Tool to determine inputs and outputs of a grit file.
'''

import optparse
import os
import posixpath
import types
import sys
from grit import grd_reader
from grit import util

##############################################################################
# os.path.relpath is python 2.6 only. Some bots still run 2.5 only, so I took
# the relpath implementation from the python source.
# TODO(thakis): Once we're on 2.6 everywhere, remove this and use
# os.path.relpath directly.

# http://docs.python.org/license.html
# PSF LICENSE AGREEMENT FOR PYTHON 2.7.1
#
# 1. This LICENSE AGREEMENT is between the Python Software Foundation ("PSF"),
# and the Individual or Organization ("Licensee") accessing and otherwise using
# Python 2.7.1 software in source or binary form and its associated
# documentation.
#
# 2. Subject to the terms and conditions of this License Agreement, PSF hereby
# grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce,
# analyze, test, perform and/or display publicly, prepare derivative works,
# distribute, and otherwise use Python 2.7.1 alone or in any derivative version,
# provided, however, that PSF's License Agreement and PSF's notice of copyright,
# i.e., "Copyright c 2001-2010 Python Software Foundation; All Rights Reserved"
# are retained in Python 2.7.1 alone or in any derivative version prepared by
# Licensee.
#
# 3. In the event Licensee prepares a derivative work that is based on or
# incorporates Python 2.7.1 or any part thereof, and wants to make the
# derivative work available to others as provided herein, then Licensee hereby
# agrees to include in any such work a brief summary of the changes made to
# Python 2.7.1.
#
# 4. PSF is making Python 2.7.1 available to Licensee on an "AS IS" basis. PSF
# MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF EXAMPLE,
# BUT NOT LIMITATION, PSF MAKES NO AND DISCLAIMS ANY REPRESENTATION OR WARRANTY
# OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF
# PYTHON 2.7.1 WILL NOT INFRINGE ANY THIRD PARTY RIGHTS.
#
# 5.1 PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON 2.7.1 FOR
# ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF
# MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 2.7.1, OR ANY DERIVATIVE
# THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
#
# 6. This License Agreement will automatically terminate upon a material breach
# of its terms and conditions.
#
# 7. Nothing in this License Agreement shall be deemed to create any
# relationship of agency, partnership, or joint venture between PSF and
# Licensee. This License Agreement does not grant permission to use PSF
# trademarks or trade name in a trademark sense to endorse or promote products
# or services of Licensee, or any third party.
#
# 8. By copying, installing or otherwise using Python 2.7.1, Licensee agrees to
# be bound by the terms and conditions of this License Agreement.

# http://svn.python.org/view/python/trunk/Lib/genericpath.py?view=markup
def commonprefix(m):
    "Given a list of pathnames, returns the longest common leading component"
    if not m: return ''
    s1 = min(m)
    s2 = max(m)
    for i, c in enumerate(s1):
        if c != s2[i]:
            return s1[:i]
    return s1


# http://svn.python.org/view/python/trunk/Lib/posixpath.py?view=markup
def relpath(path, start=os.path.curdir):
    """Return a relative version of a path"""

    if not path:
        raise ValueError("no path specified")

    start_list = os.path.abspath(start).split(os.path.sep)
    path_list = os.path.abspath(path).split(os.path.sep)

    # Work out how much of the filepath is shared by start and path.
    i = len(commonprefix([start_list, path_list]))

    rel_list = [os.path.pardir] * (len(start_list)-i) + path_list[i:]
    if not rel_list:
        return curdir
    return os.path.join(*rel_list)
##############################################################################


def Outputs(filename, defines):
  grd = grd_reader.Parse(
      filename, defines=defines, tags_to_ignore=set(['messages']))

  target = []
  lang_folders = {}
  # Add all explicitly-specified output files
  for output in grd.GetOutputFiles():
    path = output.GetFilename()
    target.append(path)

    if path.endswith('.h'):
      path, filename = os.path.split(path)
    if output.attrs['lang']:
      lang_folders[output.attrs['lang']] = os.path.dirname(path)

  # Add all generated files, once for each output language.
  for node in grd:
    if node.name == 'structure':
      # TODO(joi) Should remove the "if sconsdep is true" thing as it is a
      # hack - see grit/node/structure.py
      if node.HasFileForLanguage() and node.attrs['sconsdep'] == 'true':
        for lang in lang_folders:
          path = node.FileForLanguage(lang, lang_folders[lang],
                                      create_file=False,
                                      return_if_not_generated=False)
          if path:
            target.append(path)

  return [t.replace('\\', '/') for t in target]


def Inputs(filename, defines):
  grd = grd_reader.Parse(
      filename, debug=False, defines=defines, tags_to_ignore=set(['messages']))
  files = []
  for node in grd:
    if (node.name == 'structure' or node.name == 'skeleton' or
        (node.name == 'file' and node.parent and
         node.parent.name == 'translations')):
      files.append(node.GetFilePath())
    elif node.name == 'include':
      # Only include files that we actually plan on using.
      if node.SatisfiesOutputCondition():
        files.append(node.FilenameToOpen())
        # If it's a flattened node, grab inlined resources too.
        if node.attrs['flattenhtml'] == 'true':
          files.extend(node.GetHtmlResourceFilenames())

  # Add in the grit source files.  If one of these change, we want to re-run
  # grit.
  grit_root_dir = relpath(os.path.dirname(__file__), os.getcwd())
  for root, dirs, filenames in os.walk(grit_root_dir):
    grit_src = [os.path.join(root, f) for f in filenames
                if f.endswith('.py') or f == 'resource_ids']
    files.extend(grit_src)

  return [f.replace('\\', '/') for f in files]


def PrintUsage():
  print 'USAGE: ./grit_info.py --inputs [-D foo] <grd-files>..'
  print '       ./grit_info.py --outputs [-D foo] <out-prefix> <grd-files>..'


def DoMain(argv):
  parser = optparse.OptionParser()
  parser.add_option("--inputs", action="store_true", dest="inputs")
  parser.add_option("--outputs", action="store_true", dest="outputs")
  parser.add_option("-D", action="append", dest="defines", default=[])
  # grit build also supports '-E KEY=VALUE', support that to share command
  # line flags.
  parser.add_option("-E", action="append", dest="build_env", default=[])
  parser.add_option("-w", action="append", dest="whitelist_files", default=[])

  options, args = parser.parse_args(argv)

  if not len(args):
    return None

  defines = {}
  for define in options.defines:
    defines[define] = 1

  if options.inputs:
    for filename in args:
      inputs = Inputs(filename, defines)
      # Include grd file as second input (works around gyp expecting it).
      inputs = [inputs[0], filename] + inputs[1:]
      if options.whitelist_files:
        inputs.extend(options.whitelist_files)
      return '\n'.join(inputs)
  elif options.outputs:
    if len(args) < 2:
      return None

    for f in args[1:]:
      outputs = [posixpath.join(args[0], f) for f in Outputs(f, defines)]
      return '\n'.join(outputs)
  else:
    return None


def main(argv):
  result = DoMain(argv[1:])
  if result == None:
    PrintUsage()
    return 1
  print result
  return 0


if __name__ == '__main__':
  sys.exit(main(sys.argv))
