text: # coding=utf-8
  # Copyright 2014 Pants project contributors (see CONTRIBUTORS.md).
  # Licensed under the Apache License, Version 2.0 (see LICENSE).
  
  from __future__ import (absolute_import, division, generators, nested_scopes, print_function,
                          unicode_literals, with_statement)
  
  import os
  
  from pants.backend.jvm.tasks.jvm_compile.analysis_tools import AnalysisTools
  from pants.backend.jvm.tasks.jvm_compile.java.jmake_analysis import JMakeAnalysis
  from pants.backend.jvm.tasks.jvm_compile.java.jmake_analysis_parser import JMakeAnalysisParser
  from pants.backend.jvm.tasks.jvm_compile.jvm_compile import JvmCompile
  from pants.base.build_environment import get_buildroot
  from pants.base.exceptions import TaskError
  from pants.base.workunit import WorkUnitLabel
  from pants.util.dirutil import relativize_paths, safe_mkdir
  
  
  # From http://kenai.com/projects/jmake/sources/mercurial/content
  #  /src/com/sun/tools/jmake/Main.java?rev=26
  # Main.mainExternal docs.
  
  _JMAKE_ERROR_CODES = {
     -1: 'invalid command line option detected',
     -2: 'error reading command file',
     -3: 'project database corrupted',
     -4: 'error initializing or calling the compiler',
     -5: 'compilation error',
     -6: 'error parsing a class file',
     -7: 'file not found',
     -8: 'I/O exception',
     -9: 'internal jmake exception',
    -10: 'deduced and actual class name mismatch',
    -11: 'invalid source file extension',
    -12: 'a class in a JAR is found dependent on a class with the .java source',
    -13: 'more than one entry for the same class is found in the project',
    -20: 'internal Java error (caused by java.lang.InternalError)',
    -30: 'internal Java error (caused by java.lang.RuntimeException).'
  }
  # When executed via a subprocess return codes will be treated as unsigned
  _JMAKE_ERROR_CODES.update((256 + code, msg) for code, msg in _JMAKE_ERROR_CODES.items())
  
  
  class JmakeCompile(JvmCompile):
    """Compile Java code using JMake."""
    _name = 'java'
    _file_suffix = '.java'
    _supports_concurrent_execution = False
  
    _JMAKE_MAIN = 'org.pantsbuild.jmake.Main'
  
    @classmethod
    def get_args_default(cls, bootstrap_option_values):
      workdir_gen = os.path.relpath(os.path.join(bootstrap_option_values.pants_workdir, 'gen'),
                                    get_buildroot())
      return ('-C-encoding', '-CUTF-8', '-C-g', '-C-Tcolor',
              # Don't warn for generated code.
              '-C-Tnowarnprefixes',
              '-C{0}'.format(workdir_gen),
              # Suppress warning for annotations with no processor - we know there are many of these!
              '-C-Tnowarnregex', '-C^(warning: )?No processor claimed any of these annotations: .*')
  
    @classmethod
    def get_warning_args_default(cls):
      return ('-C-Xlint:all', '-C-Xlint:-serial', '-C-Xlint:-path', '-C-deprecation')
  
    @classmethod
    def get_no_warning_args_default(cls):
      return ('-C-Xlint:none', '-C-nowarn')
  
    @classmethod
    def register_options(cls, register):
      super(JmakeCompile, cls).register_options(register)
      register('--use-jmake', advanced=True, action='store_true', default=True,
               fingerprint=True,
               help='Use jmake to compile Java targets')
      register('--source', advanced=True, fingerprint=True,
               help='Provide source compatibility with this release. Overrides the jvm platform '
                    'source.',
               deprecated_hint='The -source arg to javac should be specified by the jvm-platform.',
               deprecated_version='0.0.43')
      register('--target', advanced=True, fingerprint=True,
               help='Generate class files for this JVM version. Overrides the jvm platform target.',
               deprecated_hint='The -target arg to javac should be specified by the jvm-platform.',
               deprecated_version='0.0.43')
      cls.register_jvm_tool(register, 'jmake')
      cls.register_jvm_tool(register, 'java-compiler')
  
    def select(self, target):
      return self.get_options().use_jmake and super(JmakeCompile, self).select(target)
  
    def __init__(self, *args, **kwargs):
      super(JmakeCompile, self).__init__(*args, **kwargs)
      self.set_distribution(jdk=True)
  
      self._buildroot = get_buildroot()
  
      # The depfile is generated by org.pantsbuild.tools.compiler.Compiler
      # and includes information about package-private classes -- e.g.
      # the case where Foo.java also defines class Bar.  This allows jmake
      # to correctly include these files in its analysis.
      self._depfile_folder = os.path.join(self.workdir, 'jmake-depfiles')
  
    @property
    def _depfile(self):
      safe_mkdir(self._depfile_folder)
      return os.path.join(self._depfile_folder, 'global_depfile')
  
    def create_analysis_tools(self):
      return AnalysisTools(self.context.java_home, JMakeAnalysisParser(), JMakeAnalysis)
  
    def compile(self, args, classpath, sources, classes_output_dir, upstream_analysis, analysis_file,
                log_file, settings):
      relative_classpath = relativize_paths(classpath, self._buildroot)
      jmake_classpath = self.tool_classpath('jmake')
      args = [
        '-classpath', ':'.join(relative_classpath),
        '-d', classes_output_dir,
        '-pdb', analysis_file,
        '-pdb-text-format',
        ]
      # TODO: This file should always exist for modern jmake installs; this check should
      # be removed via a Task-level identity bump after:
      # https://github.com/pantsbuild/pants/issues/1351
      if os.path.exists(self._depfile):
        args.extend(['-depfile', self._depfile])
  
      compiler_classpath = self.tool_classpath('java-compiler')
      args.extend([
        '-jcpath', ':'.join(compiler_classpath),
        '-jcmainclass', 'org.pantsbuild.tools.compiler.Compiler',
        ])
  
      if not self.get_options().colors:
        filtered_args = filter(lambda arg: not arg == '-C-Tcolor', self._args)
      else:
        filtered_args = self._args
      args.extend(filtered_args)
      args.extend(settings.args)
  
      if '-C-source' in args:
        raise TaskError("Set the source Java version with the 'source' or with the jvm platform, not "
                        "in 'args'.")
      if '-C-target' in args:
        raise TaskError("Set the target JVM version with the 'target' option or with the jvm "
                        "platform, not in 'args'.")
  
      if self.get_options().source or self.get_options().target:
        self.context.log.warn('--compile-java-source and --compile-java-target trample and override '
                              'target jvm platform settings, and probably should not be used except '
                              'for testing.')
  
      source_level = self.get_options().source or settings.source_level
      target_level = self.get_options().target or settings.target_level
      if source_level:
        args.extend(['-C-source', '-C{0}'.format(source_level)])
      if target_level:
        args.extend(['-C-target', '-C{0}'.format(target_level)])
  
      args.append('-C-Tdependencyfile')
      args.append('-C{}'.format(self._depfile))
  
      jvm_options = list(self._jvm_options)
  
      args.extend(sources)
      result = self.runjava(classpath=jmake_classpath,
                            main=JmakeCompile._JMAKE_MAIN,
                            jvm_options=jvm_options,
                            args=args,
                            workunit_name='jmake',
                            workunit_labels=[WorkUnitLabel.COMPILER])
      if result:
        default_message = 'Unexpected error - JMake returned {}'.format(result)
        raise TaskError(_JMAKE_ERROR_CODES.get(result, default_message))
  