# Copyright 2017 The Cobalt Authors. All Rights Reserved.
#
# 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.
"""Defines abstract build tools."""

import abc


class Tool(object):
  """A base class for all build tools.

  Build tools are used to generate Ninja files.
  """

  __metaclass__ = abc.ABCMeta

  @abc.abstractmethod
  def IsPlatformAgnostic(self):
    """Whether an output of a tool depends on a platform.

    Platform-agnostic tools are only processed once, even if they are provided
    by both target and host toolchains.
    """
    pass

  @abc.abstractmethod
  def GetRuleName(self):
    """Returns a name of a Ninja rule.

    Used as a base name for several Ninja variables, such as path, flags, etc.

    Returns:
      None if a tool doesn't have a corresponding rule.
    """
    pass

  # TODO: Inline path in a command and get rid of this method.
  @abc.abstractmethod
  def GetPath(self):
    """Returns a full path to a tool.

    Returns:
      None if a tool doesn't have a corresponding rule.
    """
    pass

  # TODO: Inline extra flags in a command and get rid of this method.
  @abc.abstractmethod
  def GetExtraFlags(self):
    """Returns tool flags specific to a platform.

    These flags are common for all tool invocations.

    Returns:
      None if a tool doesn't have a corresponding rule.
    """
    pass

  @abc.abstractmethod
  def GetMaxConcurrentProcesses(self):
    """Restricts an amount of concurrently running tool instances.

    See 'depth' at https://ninja-build.org/manual.html#ref_pool for details.

    Returns:
      None if a tool doesn't have a corresponding rule or doesn't restrict an
      amount of concurrently running instances.
    """
    pass

  @abc.abstractmethod
  def GetCommand(self, path, extra_flags, flags, shell):
    """Returns a command that invokes a tool.

    See 'command' at https://ninja-build.org/manual.html#ref_rule for details.

    Args:
      path: A variable that contains a path to a tool.
      extra_flags: A variable that contains tool flags specific to a platform.
      flags: A variable that contains tool flags specific to a target.
      shell: The shell that will be used to interpret the command.

    Returns:
      None if a tool doesn't have a corresponding rule.
    """
    pass

  @abc.abstractmethod
  def GetDescription(self):
    """Returns a human-readable description of an action performed by a tool.

    See 'description' at https://ninja-build.org/manual.html#ref_rule for
    details.

    Returns:
      None if a tool doesn't have a corresponding rule.
    """
    pass

  @abc.abstractmethod
  def GetHeaderDependenciesFilePath(self):
    """Returns a path to a Makefile that contains implicit dependencies.

    See 'depfile' at https://ninja-build.org/manual.html#ref_rule for details.

    Returns:
      None if a tool doesn't generate implicit dependencies.
    """
    pass

  @abc.abstractmethod
  def GetHeaderDependenciesFormat(self):
    """Specifies special dependency processing by Ninja.

    See 'deps' at https://ninja-build.org/manual.html#ref_rule for details.

    Returns:
      None if a tool doesn't generate implicit dependencies.
    """
    pass

  @abc.abstractmethod
  def GetRspFilePath(self):
    """Returns a full path to a response file.

    See 'rspfile' at https://ninja-build.org/manual.html#ref_rule for details.

    Returns:
      None if a response file is not used.
    """
    pass

  @abc.abstractmethod
  def GetRspFileContent(self):
    """Returns a content of a response file.

    See 'rspfile_content' at https://ninja-build.org/manual.html#ref_rule for
    details.

    Returns:
      None if a response file is not used.
    """
    pass


class CCompiler(Tool):
  """Compiles C sources."""

  def IsPlatformAgnostic(self):
    return False

  def GetRuleName(self):
    return 'compile_c'

  @abc.abstractmethod
  def GetFlags(self, defines, include_dirs, cflags):
    """Returns tool flags specific to a target.

    This method translates platform-agnostic concepts into a command line
    arguments understood by a tool.

    Args:
      defines: A list of preprocessor defines in "NAME=VALUE" format.
      include_dirs: A list of header search directories.
      cflags: A list of GCC-style command-line flags. See
        https://gcc.gnu.org/onlinedocs/gcc/Invoking-GCC.html#Invoking-GCC for
        details.

    Returns:
      A list of unquoted strings, one for each flag. It is a responsibility of a
      caller to quote flags that contain special characters (as determined by a
      shell) before passing to a tool.
    """
    pass


class CxxCompiler(Tool):
  """Compiles C++ sources."""

  def IsPlatformAgnostic(self):
    return False

  def GetRuleName(self):
    return 'compile_cxx'

  @abc.abstractmethod
  def GetFlags(self, defines, include_dirs, cflags):
    """Returns tool flags specific to a target.

    This method translates platform-agnostic concepts into a command line
    arguments understood by a tool.

    Args:
      defines: A list of preprocessor defines in "NAME=VALUE" format.
      include_dirs: A list of header search directories.
      cflags: A list of GCC-style command-line flags. See
        https://gcc.gnu.org/onlinedocs/gcc/Invoking-GCC.html#Invoking-GCC for
        details.

    Returns:
      A list of unquoted strings, one for each flag. It is a responsibility of a
      caller to quote flags that contain special characters (as determined by a
      shell) before passing to a tool.
    """
    pass


class ObjectiveCxxCompiler(Tool):
  """Compiles Objective-C++ sources."""

  def IsPlatformAgnostic(self):
    return False

  def GetRuleName(self):
    return 'compile_objective_cxx'

  @abc.abstractmethod
  def GetFlags(self, defines, include_dirs, cflags):
    """Returns tool flags specific to a target.

    This method translates platform-agnostic concepts into a command line
    arguments understood by a tool.

    Args:
      defines: A list of preprocessor defines in "NAME=VALUE" format.
      include_dirs: A list of header search directories.
      cflags: A list of GCC-style command-line flags. See
        https://gcc.gnu.org/onlinedocs/gcc/Invoking-GCC.html#Invoking-GCC for
        details.

    Returns:
      A list of unquoted strings, one for each flag. It is a responsibility of a
      caller to quote flags that contain special characters (as determined by a
      shell) before passing to a tool.
    """
    pass


class AssemblerWithCPreprocessor(Tool):
  """Compiles assembler sources that contain C preprocessor directives."""

  def IsPlatformAgnostic(self):
    return False

  def GetRuleName(self):
    return 'assemble'

  @abc.abstractmethod
  def GetFlags(self, defines, include_dirs, cflags):
    """Returns tool flags specific to a target.

    This method translates platform-agnostic concepts into a command line
    arguments understood by a tool.

    Args:
      defines: A list of preprocessor defines in "NAME=VALUE" format.
      include_dirs: A list of header search directories.
      cflags: A list of GCC-style command-line flags. See
        https://gcc.gnu.org/onlinedocs/gcc/Invoking-GCC.html#Invoking-GCC for
        details.

    Returns:
      A list of unquoted strings, one for each flag. It is a responsibility of a
      caller to quote flags that contain special characters (as determined by a
      shell) before passing to a tool.
    """
    pass


class StaticLinker(Tool):
  """Creates self-contained archives."""

  def IsPlatformAgnostic(self):
    return False

  def GetRuleName(self):
    return 'link_static'

  def GetHeaderDependenciesFilePath(self):
    # Only applicable to C family compilers.
    return None

  def GetHeaderDependenciesFormat(self):
    # Only applicable to C family compilers.
    return None


class StaticThinLinker(Tool):
  """Creates thin archives using GNU ar."""

  def IsPlatformAgnostic(self):
    return False

  def GetRuleName(self):
    return 'link_static_thin'

  def GetHeaderDependenciesFilePath(self):
    # Only applicable to C family compilers.
    return None

  def GetHeaderDependenciesFormat(self):
    # Only applicable to C family compilers.
    return None


class ExecutableLinker(Tool):
  """Links executables."""

  def IsPlatformAgnostic(self):
    return False

  def GetRuleName(self):
    return 'link_executable'

  def GetHeaderDependenciesFilePath(self):
    # Only applicable to C family compilers.
    return None

  def GetHeaderDependenciesFormat(self):
    # Only applicable to C family compilers.
    return None

  @abc.abstractmethod
  def GetFlags(self, ldflags):
    """Returns tool flags specific to a target.

    This method translates platform-agnostic concepts into a command line
    arguments understood by a tool.

    Args:
      ldflags: A list of GCC-style command-line flags. See
        https://gcc.gnu.org/onlinedocs/gcc/Link-Options.html#Link-Options for
        details.

    Returns:
      A list of unquoted strings, one for each flag. It is a responsibility of a
      caller to quote flags that contain special characters (as determined by a
      shell) before passing to a tool.
    """
    pass


class SharedLibraryLinker(Tool):
  """Links shared libraries."""

  def IsPlatformAgnostic(self):
    return False

  def GetRuleName(self):
    return 'link_shared'

  def GetHeaderDependenciesFilePath(self):
    # Only applicable to C family compilers.
    return None

  def GetHeaderDependenciesFormat(self):
    # Only applicable to C family compilers.
    return None

  @abc.abstractmethod
  def GetFlags(self, ldflags):
    """Returns tool flags specific to a target.

    This method translates platform-agnostic concepts into a command line
    arguments understood by a tool.

    Args:
      ldflags: A list of GCC-style command-line flags. See
        https://gcc.gnu.org/onlinedocs/gcc/Link-Options.html#Link-Options for
        details.

    Returns:
      A list of unquoted strings, one for each flag. It is a responsibility of a
      caller to quote flags that contain special characters (as determined by a
      shell) before passing to a tool.
    """
    pass


class Stamp(Tool):
  """Updates the access and modification times of a file to the current time."""

  def IsPlatformAgnostic(self):
    return True

  def GetRuleName(self):
    return 'stamp'

  def GetHeaderDependenciesFilePath(self):
    # Only applicable to C family compilers.
    return None

  def GetHeaderDependenciesFormat(self):
    # Only applicable to C family compilers.
    return None


class Copy(Tool):
  """Copies individual files."""

  def IsPlatformAgnostic(self):
    return True

  def GetRuleName(self):
    return 'copy'

  def GetHeaderDependenciesFilePath(self):
    # Only applicable to C family compilers.
    return None

  def GetHeaderDependenciesFormat(self):
    # Only applicable to C family compilers.
    return None


class Shell(Tool):
  """Constructs command lines."""

  def IsPlatformAgnostic(self):
    return True

  def GetRuleName(self):
    # Shell should not be used by Ninja, only by other tools.
    return None

  def GetPath(self):
    # Shell should not be used by Ninja, only by other tools.
    return None

  def GetExtraFlags(self):
    # Shell should not be used by Ninja, only by other tools.
    return None

  def GetMaxConcurrentProcesses(self):
    # Shell should not be used by Ninja, only by other tools.
    return None

  def GetCommand(self, path, extra_flags, flags, shell):
    # Shell should not be used by Ninja, only by other tools.
    return None

  def GetDescription(self):
    # Shell should not be used by Ninja, only by other tools.
    return None

  def GetHeaderDependenciesFilePath(self):
    # Shell should not be used by Ninja, only by other tools.
    return None

  def GetHeaderDependenciesFormat(self):
    # Shell should not be used by Ninja, only by other tools.
    return None

  def GetRspFilePath(self):
    # Shell should not be used by Ninja, only by other tools.
    return None

  def GetRspFileContent(self):
    # Shell should not be used by Ninja, only by other tools.
    return None

  @abc.abstractmethod
  def MaybeQuoteArgument(self, argument):
    """Quotes a string so that shell could interpret it as a single argument.

    Args:
      argument: A string that can contain arbitrary characters.

    Returns:
      A quoted and appropriately escaped string. Returns the original string
      if no processing is necessary.
    """
    pass

  @abc.abstractmethod
  def Join(self, command):
    """Joins a sequence of unquoted command line terms into a string.

    Args:
      command: An unquoted, unescaped sequence of command line terms.

    Returns:
      A quoted and appropriately escaped string for the same command line.
    """
    pass

  @abc.abstractmethod
  def And(self, *commands):
    """Joins a sequence of command lines with the AND shell operator.

    Args:
      *commands: A sequence of either quoted strings or unquoted sequences of
                 command line terms that make up the command lines to be ANDed.

    Returns:
      A quoted and appropriately escaped string for the ANDed command line.
    """
    pass

  @abc.abstractmethod
  def Or(self, *commands):
    """Joins a sequence of command lines with the OR shell operator.

    Args:
      *commands: A sequence of either quoted strings or unquoted sequences of
                 command line terms that make up the command lines to be ORed.

    Returns:
      A quoted and appropriately escaped string for the ORed command line.
    """
    pass
