text: """ xgettext tool
  
  Tool specific initialization of `xgettext` tool.
  """
  
  # __COPYRIGHT__
  #
  # Permission is hereby granted, free of charge, to any person obtaining
  # a copy of this software and associated documentation files (the
  # "Software"), to deal in the Software without restriction, including
  # without limitation the rights to use, copy, modify, merge, publish,
  # distribute, sublicense, and/or sell copies of the Software, and to
  # permit persons to whom the Software is furnished to do so, subject to
  # the following conditions:
  #
  # The above copyright notice and this permission notice shall be included
  # in all copies or substantial portions of the Software.
  #
  # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
  # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  
  __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
  
  
  #############################################################################
  class _CmdRunner(object):
      """ Callable object, which runs shell command storing its stdout and stderr to
      variables. It also provides `strfunction()` method, which shall be used by
      scons Action objects to print command string. """
  
      def __init__(self, command, commandstr=None):
          self.out = None
          self.err = None
          self.status = None
          self.command = command
          self.commandstr = commandstr
  
      def __call__(self, target, source, env):
          import SCons.Action
          import subprocess
          import os
          import sys
          kw = {
              'stdin': 'devnull',
              'stdout': subprocess.PIPE,
              'stderr': subprocess.PIPE,
              'universal_newlines': True,
              'shell': True
          }
          command = env.subst(self.command, target=target, source=source)
          proc = SCons.Action._subproc(env, command, **kw)
          self.out, self.err = proc.communicate()
          self.status = proc.wait()
          if self.err:
              sys.stderr.write(unicode(self.err))
          return self.status
  
      def strfunction(self, target, source, env):
          import os
          comstr = self.commandstr
          if env.subst(comstr, target=target, source=source) == "":
              comstr = self.command
          s = env.subst(comstr, target=target, source=source)
          return s
  
  
  #############################################################################
  
  #############################################################################
  def _update_pot_file(target, source, env):
      """ Action function for `POTUpdate` builder """
      import re
      import os
      import SCons.Action
      nop = lambda target, source, env: 0
  
      # Save scons cwd and os cwd (NOTE: they may be different. After the job, we
      # revert each one to its original state).
      save_cwd = env.fs.getcwd()
      save_os_cwd = os.getcwd()
      chdir = target[0].dir
      chdir_str = repr(chdir.get_abspath())
      # Print chdir message (employ SCons.Action.Action for that. It knows better
      # than me how to to this correctly).
      env.Execute(SCons.Action.Action(nop, "Entering " + chdir_str))
      # Go to target's directory and do our job
      env.fs.chdir(chdir, 1)  # Go into target's directory
      try:
          cmd = _CmdRunner('$XGETTEXTCOM', '$XGETTEXTCOMSTR')
          action = SCons.Action.Action(cmd, strfunction=cmd.strfunction)
          status = action([target[0]], source, env)
      except:
          # Something went wrong.
          env.Execute(SCons.Action.Action(nop, "Leaving " + chdir_str))
          # Revert working dirs to previous state and re-throw exception.
          env.fs.chdir(save_cwd, 0)
          os.chdir(save_os_cwd)
          raise
      # Print chdir message.
      env.Execute(SCons.Action.Action(nop, "Leaving " + chdir_str))
      # Revert working dirs to previous state.
      env.fs.chdir(save_cwd, 0)
      os.chdir(save_os_cwd)
      # If the command was not successfull, return error code.
      if status: return status
  
      new_content = cmd.out
  
      if not new_content:
          # When xgettext finds no internationalized messages, no *.pot is created
          # (because we don't want to bother translators with empty POT files).
          needs_update = False
          explain = "no internationalized messages encountered"
      else:
          if target[0].exists():
              # If the file already exists, it's left unaltered unless its messages
              # are outdated (w.r.t. to these recovered by xgettext from sources).
              old_content = target[0].get_text_contents()
              re_cdate = re.compile(r'^"POT-Creation-Date: .*"$[\r\n]?', re.M)
              old_content_nocdate = re.sub(re_cdate, "", old_content)
              new_content_nocdate = re.sub(re_cdate, "", new_content)
              if (old_content_nocdate == new_content_nocdate):
                  # Messages are up-to-date
                  needs_update = False
                  explain = "messages in file found to be up-to-date"
              else:
                  # Messages are outdated
                  needs_update = True
                  explain = "messages in file were outdated"
          else:
              # No POT file found, create new one
              needs_update = True
              explain = "new file"
      if needs_update:
          # Print message employing SCons.Action.Action for that.
          msg = "Writing " + repr(str(target[0])) + " (" + explain + ")"
          env.Execute(SCons.Action.Action(nop, msg))
          f = open(str(target[0]), "w")
          f.write(new_content)
          f.close()
          return 0
      else:
          # Print message employing SCons.Action.Action for that.
          msg = "Not writing " + repr(str(target[0])) + " (" + explain + ")"
          env.Execute(SCons.Action.Action(nop, msg))
          return 0
  
  
  #############################################################################
  
  #############################################################################
  from SCons.Builder import BuilderBase
  
  
  #############################################################################
  class _POTBuilder(BuilderBase):
      def _execute(self, env, target, source, *args):
          if not target:
              if 'POTDOMAIN' in env and env['POTDOMAIN']:
                  domain = env['POTDOMAIN']
              else:
                  domain = 'messages'
              target = [domain]
          return BuilderBase._execute(self, env, target, source, *args)
  
  
  #############################################################################
  
  #############################################################################
  def _scan_xgettext_from_files(target, source, env, files=None, path=None):
      """ Parses `POTFILES.in`-like file and returns list of extracted file names.
      """
      import re
      import SCons.Util
      import SCons.Node.FS
  
      if files is None:
          return 0
      if not SCons.Util.is_List(files):
          files = [files]
  
      if path is None:
          if 'XGETTEXTPATH' in env:
              path = env['XGETTEXTPATH']
          else:
              path = []
      if not SCons.Util.is_List(path):
          path = [path]
  
      path = SCons.Util.flatten(path)
  
      dirs = ()
      for p in path:
          if not isinstance(p, SCons.Node.FS.Base):
              if SCons.Util.is_String(p):
                  p = env.subst(p, source=source, target=target)
              p = env.arg2nodes(p, env.fs.Dir)
          dirs += tuple(p)
      # cwd is the default search path (when no path is defined by user)
      if not dirs:
          dirs = (env.fs.getcwd(),)
  
      # Parse 'POTFILE.in' files.
      re_comment = re.compile(r'^#[^\n\r]*$\r?\n?', re.M)
      re_emptyln = re.compile(r'^[ \t\r]*$\r?\n?', re.M)
      re_trailws = re.compile(r'[ \t\r]+$')
      for f in files:
          # Find files in search path $XGETTEXTPATH
          if isinstance(f, SCons.Node.FS.Base) and f.rexists():
              contents = f.get_text_contents()
              contents = re_comment.sub("", contents)
              contents = re_emptyln.sub("", contents)
              contents = re_trailws.sub("", contents)
              depnames = contents.splitlines()
              for depname in depnames:
                  depfile = SCons.Node.FS.find_file(depname, dirs)
                  if not depfile:
                      depfile = env.arg2nodes(depname, dirs[0].File)
                  env.Depends(target, depfile)
      return 0
  
  
  #############################################################################
  
  #############################################################################
  def _pot_update_emitter(target, source, env):
      """ Emitter function for `POTUpdate` builder """
      from SCons.Tool.GettextCommon import _POTargetFactory
      import SCons.Util
      import SCons.Node.FS
  
      if 'XGETTEXTFROM' in env:
          xfrom = env['XGETTEXTFROM']
      else:
          return target, source
      if not SCons.Util.is_List(xfrom):
          xfrom = [xfrom]
  
      xfrom = SCons.Util.flatten(xfrom)
  
      # Prepare list of 'POTFILE.in' files.
      files = []
      for xf in xfrom:
          if not isinstance(xf, SCons.Node.FS.Base):
              if SCons.Util.is_String(xf):
                  # Interpolate variables in strings
                  xf = env.subst(xf, source=source, target=target)
              xf = env.arg2nodes(xf)
          files.extend(xf)
      if files:
          env.Depends(target, files)
          _scan_xgettext_from_files(target, source, env, files)
      return target, source
  
  
  #############################################################################
  
  #############################################################################
  from SCons.Environment import _null
  
  
  #############################################################################
  def _POTUpdateBuilderWrapper(env, target=None, source=_null, **kw):
      return env._POTUpdateBuilder(target, source, **kw)
  
  
  #############################################################################
  
  #############################################################################
  def _POTUpdateBuilder(env, **kw):
      """ Creates `POTUpdate` builder object """
      import SCons.Action
      from SCons.Tool.GettextCommon import _POTargetFactory
      kw['action'] = SCons.Action.Action(_update_pot_file, None)
      kw['suffix'] = '$POTSUFFIX'
      kw['target_factory'] = _POTargetFactory(env, alias='$POTUPDATE_ALIAS').File
      kw['emitter'] = _pot_update_emitter
      return _POTBuilder(**kw)
  
  
  #############################################################################
  
  #############################################################################
  def generate(env, **kw):
      """ Generate `xgettext` tool """
      import SCons.Util
      from SCons.Tool.GettextCommon import RPaths, _detect_xgettext
  
      try:
          env['XGETTEXT'] = _detect_xgettext(env)
      except:
          env['XGETTEXT'] = 'xgettext'
      # NOTE: sources="$SOURCES" would work as well. However, we use following
      # construction to convert absolute paths provided by scons onto paths
      # relative to current working dir. Note, that scons expands $SOURCE(S) to
      # absolute paths for sources $SOURCE(s) outside of current subtree (e.g. in
      # "../"). With source=$SOURCE these absolute paths would be written to the
      # resultant *.pot file (and its derived *.po files) as references to lines in
      # source code (e.g. referring lines in *.c files). Such references would be
      # correct (e.g. in poedit) only on machine on which *.pot was generated and
      # would be of no use on other hosts (having a copy of source code located
      # in different place in filesystem).
      sources = '$( ${_concat( "", SOURCES, "", __env__, XgettextRPaths, TARGET' \
                + ', SOURCES)} $)'
  
      # NOTE: the output from $XGETTEXTCOM command must go to stdout, not to a file.
      # This is required by the POTUpdate builder's action.
      xgettextcom = '$XGETTEXT $XGETTEXTFLAGS $_XGETTEXTPATHFLAGS' \
                    + ' $_XGETTEXTFROMFLAGS -o - ' + sources
  
      xgettextpathflags = '$( ${_concat( XGETTEXTPATHPREFIX, XGETTEXTPATH' \
                          + ', XGETTEXTPATHSUFFIX, __env__, RDirs, TARGET, SOURCES)} $)'
      xgettextfromflags = '$( ${_concat( XGETTEXTFROMPREFIX, XGETTEXTFROM' \
                          + ', XGETTEXTFROMSUFFIX, __env__, target=TARGET, source=SOURCES)} $)'
  
      env.SetDefault(
          _XGETTEXTDOMAIN='${TARGET.filebase}',
          XGETTEXTFLAGS=[],
          XGETTEXTCOM=xgettextcom,
          XGETTEXTCOMSTR='',
          XGETTEXTPATH=[],
          XGETTEXTPATHPREFIX='-D',
          XGETTEXTPATHSUFFIX='',
          XGETTEXTFROM=None,
          XGETTEXTFROMPREFIX='-f',
          XGETTEXTFROMSUFFIX='',
          _XGETTEXTPATHFLAGS=xgettextpathflags,
          _XGETTEXTFROMFLAGS=xgettextfromflags,
          POTSUFFIX=['.pot'],
          POTUPDATE_ALIAS='pot-update',
          XgettextRPaths=RPaths(env)
      )
      env.Append(BUILDERS={
          '_POTUpdateBuilder': _POTUpdateBuilder(env)
      })
      env.AddMethod(_POTUpdateBuilderWrapper, 'POTUpdate')
      env.AlwaysBuild(env.Alias('$POTUPDATE_ALIAS'))
  
  
  #############################################################################
  
  #############################################################################
  def exists(env):
      """ Check, whether the tool exists """
      from SCons.Tool.GettextCommon import _xgettext_exists
      try:
          return _xgettext_exists(env)
      except:
          return False
  
  #############################################################################
  
  # Local Variables:
  # tab-width:4
  # indent-tabs-mode:nil
  # End:
  # vim: set expandtab tabstop=4 shiftwidth=4:
  