text: #
  #
  # Licensed to the Apache Software Foundation (ASF) under one
  # or more contributor license agreements.  See the NOTICE file
  # distributed with this work for additional information
  # regarding copyright ownership.  The ASF licenses this file
  # to you 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.
  #
  #
  #
  # gen_win.py -- base class for generating windows projects
  #
  
  import os
  from hashlib import md5 as hashlib_md5
  import sys
  import fnmatch
  import re
  import subprocess
  import glob
  import string
  import generator.swig.header_wrappers
  import generator.swig.checkout_swig_header
  import generator.swig.external_runtime
  import gen_win_dependencies
  
  if sys.version_info[0] >= 3:
    # Python >=3.0
    from io import StringIO
  else:
    # Python <3.0
    try:
      from cStringIO import StringIO
    except ImportError:
      from StringIO import StringIO
  
  import gen_base
  import ezt
  
  class WinGeneratorBase(gen_win_dependencies.GenDependenciesBase):
    "Base class for all Windows project files generators"
  
    def __init__(self, fname, verfname, options, subdir):
      """
      Do some Windows specific setup
  
      Build the list of Platforms & Configurations &
      create the necessary paths
      """
  
      # Initialize parent
      gen_win_dependencies.GenDependenciesBase.__init__(self, fname, verfname,
                                                        options, find_libs=False)
  
      # On Windows we create svn_private_config.h in the output directory since
      # r1370526.
      #
      # Without this replacement all projects include a not-existing file,
      # which makes the MSBuild calculation to see whether a project is changed
      # far more expensive than necessary.
      self.private_built_includes.append('$(Configuration)/svn_private_config.h')
      self.private_built_includes.remove('subversion/svn_private_config.h')
  
      if subdir == 'vcnet-vcproj':
        print('Generating for Visual Studio %s\n' % self.vs_version)
  
      self.find_libraries(True)
  
      # Print list of identified libraries
      printed = []
      for lib in sorted(self._libraries.values(),
                        key = lambda s: (s.internal, s.name)):
        if lib.name in printed:
          continue
        printed.append(lib.name)
        if lib.internal:
          print('Using bundled %s %s' % (lib.name, lib.version))
        else:
          print('Found %s %s' % (lib.name, lib.version))
  
      #Make some files for the installer so that we don't need to
      #require sed or some other command to do it
      ### GJS: don't do this right now
      if 0:
        buf = open(os.path.join("packages","win32-innosetup","svn.iss.in"), 'rb').read()
        buf = buf.replace("@VERSION@", "0.16.1+").replace("@RELEASE@", "4365")
        buf = buf.replace("@DBBINDLL@", self.dbbindll)
        svnissrel = os.path.join("packages","win32-innosetup","svn.iss.release")
        svnissdeb = os.path.join("packages","win32-innosetup","svn.iss.debug")
        if self.write_file_if_changed(svnissrel, buf.replace("@CONFIG@", "Release")):
          print('Wrote %s' % svnissrel)
        if self.write_file_if_changed(svnissdeb, buf.replace("@CONFIG@", "Debug")):
          print('Wrote %s' % svnissdeb)
  
      #Make the project files directory if it doesn't exist
      #TODO win32 might not be the best path as win64 stuff will go here too
      self.projfilesdir=os.path.join("build","win32",subdir)
      self.rootpath = self.find_rootpath()
      if not os.path.exists(self.projfilesdir):
        os.makedirs(self.projfilesdir)
  
      # Generate the build_locale.bat file
      if self.enable_nls:
        pofiles = []
        for po in os.listdir(os.path.join('subversion', 'po')):
          if fnmatch.fnmatch(po, '*.po'):
            pofiles.append(POFile(po[:-3]))
  
        data = {'pofiles': pofiles}
        self.write_with_template(os.path.join(self.projfilesdir,
                                              'build_locale.bat'),
                                 'templates/build_locale.ezt', data)
  
      #Here we can add additional platforms to compile for
      self.platforms = ['Win32']
  
      # VC 2002 and VC 2003 only allow a single platform per project file
      if subdir == 'vcnet-vcproj':
        if self.vcproj_version != '7.00' and self.vcproj_version != '7.10':
          self.platforms = ['Win32','x64']
  
      #Here we can add additional modes to compile for
      self.configs = ['Debug','Release']
  
      if 'swig' in self._libraries:
        # Generate SWIG header wrappers and external runtime
        for swig in (generator.swig.header_wrappers,
                     generator.swig.checkout_swig_header,
                     generator.swig.external_runtime):
          swig.Generator(self.conf, self.swig_exe).write()
      else:
        print("%s not found; skipping SWIG file generation..." % self.swig_exe)
  
    def errno_filter(self, codes):
      "Callback for gen_base.write_errno_table()."
      # Filter out python's SOC* codes, which alias the windows API names.
      return set(filter(lambda code: not (10000 <= code <= 10100), codes))
  
    def find_rootpath(self):
      "Gets the root path as understand by the project system"
      return os.path.relpath('.', self.projfilesdir) + "\\"
  
    def makeguid(self, data):
      "Generate a windows style GUID"
      ### blah. this function can generate invalid GUIDs. leave it for now,
      ### but we need to fix it. we can wrap the apr UUID functions, or
      ### implement this from scratch using the algorithms described in
      ### http://www.webdav.org/specs/draft-leach-uuids-guids-01.txt
  
      myhash = hashlib_md5(data).hexdigest()
  
      guid = ("{%s-%s-%s-%s-%s}" % (myhash[0:8], myhash[8:12],
                                    myhash[12:16], myhash[16:20],
                                    myhash[20:32])).upper()
      return guid
  
    def path(self, *paths):
      """Convert build path to msvc path and prepend root"""
      return self.rootpath + msvc_path_join(*list(map(msvc_path, paths)))
  
    def apath(self, path, *paths):
      """Convert build path to msvc path and prepend root if not absolute"""
      ### On Unix, os.path.isabs won't do the right thing if "item"
      ### contains backslashes or drive letters
      if os.path.isabs(path):
        return msvc_path_join(msvc_path(path), *list(map(msvc_path, paths)))
      else:
        return self.rootpath + msvc_path_join(msvc_path(path),
                                              *list(map(msvc_path, paths)))
  
    def get_install_targets(self):
      "Generate the list of targets"
  
      # Get list of targets to generate project files for
      install_targets = self.graph.get_all_sources(gen_base.DT_INSTALL) \
                        + self.projects
  
      install_targets = [x for x in install_targets if not x.when or
                                                       x.when in self._windows_when]
  
      # Don't create projects for scripts
      install_targets = [x for x in install_targets if not isinstance(x, gen_base.TargetScript)]
  
      if not self.enable_nls:
        install_targets = [x for x in install_targets if x.name != 'locale']
  
      # Drop the libsvn_fs_base target and tests if we don't have BDB
      if 'db' not in self._libraries:
        install_targets = [x for x in install_targets if x.name != 'libsvn_fs_base']
        install_targets = [x for x in install_targets if not (isinstance(x, gen_base.TargetExe)
                                                              and x.install == 'bdb-test')]
  
      # Drop the ra_serf target if we don't have serf
      if 'serf' not in self._libraries:
        install_targets = [x for x in install_targets if x.name != 'libsvn_ra_serf']
  
      # Drop the swig targets if we don't have swig or language support
      install_targets = [x for x in install_targets
                         if (not (isinstance(x, gen_base.TargetSWIG)
                                  or isinstance(x, gen_base.TargetSWIGLib)
                                  or isinstance(x, gen_base.TargetSWIGProject))
                             or (x.lang in self._libraries
                                 and 'swig' in self._libraries))]
  
      # Drop the Java targets if we don't have a JDK
      if 'java_sdk' not in self._libraries:
        install_targets = [x for x in install_targets
                                       if not (isinstance(x, gen_base.TargetJava)
                                               or isinstance(x, gen_base.TargetJavaHeaders)
                                               or x.name == '__JAVAHL__'
                                               or x.name == '__JAVAHL_TESTS__'
                                               or x.name == 'libsvnjavahl')]
  
      # Create DLL targets for libraries
      dll_targets = []
      for target in install_targets:
        if isinstance(target, gen_base.TargetLib):
          if target.msvc_fake:
            install_targets.append(self.create_fake_target(target))
          if target.msvc_export:
            if self.disable_shared:
              target.disable_shared()
            else:
              dll_targets.append(self.create_dll_target(target))
      install_targets.extend(dll_targets)
  
      # Fix up targets that can't be linked to libraries
      if not self.disable_shared:
        for target in install_targets:
          if isinstance(target, gen_base.TargetExe) and target.msvc_force_static:
  
            # Make direct dependencies of all the indirect dependencies
            linked_deps = {}
            self.get_linked_win_depends(target, linked_deps)
  
            for lk in linked_deps.keys():
              if not isinstance(lk, gen_base.TargetLib) or not lk.msvc_export:
                self.graph.add(gen_base.DT_LINK, target.name, lk)
              else:
                self.graph.remove(gen_base.DT_LINK, target.name, lk)
  
      for target in install_targets:
        target.project_guid = self.makeguid(target.name)
  
      # sort these for output stability, to watch out for regressions.
      install_targets.sort(key = lambda t: t.name)
      return install_targets
  
    def create_fake_target(self, dep):
      "Return a new target which depends on another target but builds nothing"
      section = gen_base.TargetProject.Section(gen_base.TargetProject,
                                               dep.name + "_fake",
                                               {'path': 'build/win32'}, self)
      section.create_targets()
      section.target.msvc_name = dep.msvc_name and dep.msvc_name + "_fake"
      self.graph.add(gen_base.DT_LINK, section.target.name, dep)
      dep.msvc_fake = section.target
      return section.target
  
    def create_dll_target(self, dep):
      "Return a dynamic library that depends on a static library"
      target = gen_base.TargetLib(dep.name,
                                  { 'path'      : dep.path,
                                    'msvc-name' : dep.name + "_dll" },
                                  self)
      target.msvc_export = dep.msvc_export
      target.msvc_delayload = dep.msvc_delayload
  
      # move the description from the static library target to the dll.
      target.desc = dep.desc
      dep.desc = None
  
      # The dependency should now be static.
      dep.msvc_export = None
      dep.msvc_static = True
      dep.msvc_delayload = False
  
      # Remove the 'lib' prefix, so that the static library will be called
      # svn_foo.lib
      dep.name = dep.name[3:]
      # However, its name should still be 'libsvn_foo' in Visual Studio
      dep.msvc_name = target.name
  
      # We renamed dep, so right now it has no dependencies. Because target has
      # dep's old dependencies, transfer them over to dep.
      deps = self.graph.deps[gen_base.DT_LINK]
      deps[dep.name] = deps[target.name]
  
      for key in deps.keys():
        # Link everything except tests against the dll. Tests need to be linked
        # against the static libraries because they sometimes access internal
        # library functions.
  
        if dep in deps[key]:
          deps[key].remove(dep)
          deps[key].append(target)
  
      # The dll has exactly one dependency, the static library.
      deps[target.name] = [ dep ]
  
      return target
  
    def get_configs(self, target):
      "Get the list of configurations for the project"
      configs = [ ]
      for cfg in self.configs:
        configs.append(
          ProjectItem(name=cfg,
                      lower=cfg.lower(),
                      defines=self.get_win_defines(target, cfg),
                      libdirs=self.get_win_lib_dirs(target, cfg),
                      libs=self.get_win_libs(target, cfg),
                      includes=self.get_win_includes(target, cfg),
                      forced_include_files
                              =self.get_win_forced_includes(target, cfg),
                      ))
      return configs
  
    def get_proj_sources(self, quote_path, target):
      "Get the list of source files for each project"
      sources = [ ]
  
      javac_exe = "javac"
      javah_exe = "javah"
      jar_exe = "jar"
      if self.jdk_path:
        javac_exe = os.path.join(self.jdk_path, "bin", javac_exe)
        javah_exe = os.path.join(self.jdk_path, "bin", javah_exe)
        jar_exe = os.path.join(self.jdk_path, "bin", jar_exe)
  
      if not isinstance(target, gen_base.TargetProject):
        for source, object, reldir in self.get_win_sources(target):
          cbuild = None
          ctarget = None
          cdesc = None
          cignore = None
          if isinstance(target, gen_base.TargetJavaHeaders):
            classes = self.path(target.classes)
            if self.junit_path is not None:
              classes = "%s;%s" % (classes, self.junit_path)
  
            headers = self.path(target.headers)
            classname = target.package + "." + source.class_name
  
            cbuild = "%s -verbose -force -classpath %s -d %s %s" \
                     % (self.quote(javah_exe), self.quote(classes),
                        self.quote(headers), classname)
  
            ctarget = self.path(object.filename_win)
            cdesc = "Generating %s" % (object.filename_win)
  
          elif isinstance(target, gen_base.TargetJavaClasses):
            classes = targetdir = self.path(target.classes)
            if self.junit_path is not None:
              classes = "%s;%s" % (classes, self.junit_path)
  
            sourcepath = self.path(source.sourcepath)
  
            per_project_flags = ""
  
            if target.name.find("-compat-"):
              per_project_flags += "-Xlint:-deprecation -Xlint:-dep-ann" \
                                   " -Xlint:-rawtypes"
  
            cbuild = ("%s -g -Xlint -Xlint:-options " +
                      per_project_flags +
                      " -target 1.5 -source 1.5 -classpath "
                      " %s -d %s "
                      " -sourcepath %s $(InputPath)") \
                     % tuple(map(self.quote, (javac_exe, classes,
                                              targetdir, sourcepath)))
  
  
            ctarget = self.path(object.filename)
            cdesc = "Compiling %s" % (source)
  
          rsrc = self.path(str(source))
          if quote_path and '-' in rsrc:
            rsrc = '"%s"' % rsrc
  
          if (not isinstance(source, gen_base.SourceFile)
              and cbuild is None and ctarget is None and cdesc is None
              and source in self._excluded_from_build):
            # Make sure include dependencies are excluded from the build.
            # This is an 'orrible 'ack that relies on the source being a
            # string if it's an include dependency, or a SourceFile object
            # otherwise.
            cignore = 'yes'
  
          sources.append(ProjectItem(path=rsrc, reldir=reldir, user_deps=[],
                                     custom_build=cbuild, custom_target=ctarget,
                                     custom_desc=cdesc, ignored = cignore,
                                     extension=os.path.splitext(rsrc)[1]))
  
      if isinstance(target, gen_base.TargetJavaClasses) and target.jar:
        classdir = self.path(target.classes)
        jarfile = msvc_path_join(classdir, target.jar)
        cbuild = "%s cf %s -C %s %s" \
                 % (self.quote(jar_exe), jarfile, classdir,
                    " ".join(target.packages))
        deps = [x.custom_target for x in sources]
        sources.append(ProjectItem(path='makejar', reldir='', user_deps=deps,
                                   custom_build=cbuild, custom_target=jarfile,
                                   extension=''))
  
      if isinstance(target, gen_base.TargetSWIG):
        swig_options = self.swig.opts[target.lang].split()
        swig_options.append('-DWIN32')
        swig_deps = []
  
        for include_dir in self.get_win_includes(target):
          swig_options.append("-I%s" % self.quote(include_dir))
  
        for obj in self.graph.get_sources(gen_base.DT_LINK, target.name):
          if isinstance(obj, gen_base.SWIGObject):
            for cobj in self.graph.get_sources(gen_base.DT_OBJECT, obj):
              if isinstance(cobj, gen_base.SWIGObject):
                csrc = self.path(cobj.filename)
  
                cout = csrc
  
                # included header files that the generated c file depends on
                user_deps = swig_deps[:]
  
                for iobj in self.graph.get_sources(gen_base.DT_SWIG_C, cobj):
                  isrc = self.path(str(iobj))
  
                  if not isinstance(iobj, gen_base.SWIGSource):
                    user_deps.append(isrc)
                    continue
  
                  cbuild = '%s %s -o %s $(InputPath)' \
                           % (self.swig_exe, " ".join(swig_options), cout)
  
                  cdesc = 'Generating %s' % cout
  
                  sources.append(ProjectItem(path=isrc, reldir=None,
                                             custom_build=cbuild,
                                             custom_target=csrc,
                                             custom_desc=cdesc,
                                             user_deps=user_deps,
                                             extension=''))
  
      def_file = self.get_def_file(target)
      if def_file is not None:
        gsrc = self.path("build/generator/extractor.py")
  
        deps = [self.path('build.conf')]
        for header in target.msvc_export:
          deps.append(self.path('subversion/include', header))
  
        cbuild = "%s $(InputPath) %s > %s" \
                 % (self.quote(sys.executable), " ".join(deps), def_file)
  
        cdesc = 'Generating %s ' % def_file
  
        sources.append(ProjectItem(path=gsrc, reldir=None,
                                   custom_build=cbuild,
                                   custom_target=def_file,
                                   custom_desc=cdesc,
                                   user_deps=deps,
                                   extension=''))
  
        sources.append(ProjectItem(path=def_file, reldir=None,
                                   custom_build=None, user_deps=[],
                                   extension=''))
  
      sources.sort(key = lambda x: x.path)
      return sources
  
    def get_output_name(self, target):
      if isinstance(target, gen_base.TargetExe):
        return target.name + '.exe'
      elif isinstance(target, gen_base.TargetJava):
        ### This target file is not actually built, but we need it to keep
        ### the VC Express build happy.
        return target.name
      elif isinstance(target, gen_base.TargetApacheMod):
        return target.name + '.so'
      elif isinstance(target, gen_base.TargetLib):
        if target.msvc_static:
          return '%s-%d.lib' % (target.name, self.version)
        else:
          return os.path.basename(target.filename)
      elif isinstance(target, gen_base.TargetProject):
        ### Since this target type doesn't produce any output, we shouldn't
        ### need to specify an output filename. But to keep the VC.NET template
        ### happy for now we have to return something
        return target.name + '.exe'
      elif isinstance(target, gen_base.TargetI18N):
        return target.name
  
    def get_output_pdb(self, target):
      name = self.get_output_name(target)
      name = os.path.splitext(name)
      return name[0] + '.pdb'
  
    def get_output_dir(self, target):
      if isinstance(target, gen_base.TargetJavaHeaders):
        return msvc_path("../" + target.headers)
      elif isinstance(target, gen_base.TargetJavaClasses):
        return msvc_path("../" + target.classes)
      else:
        return msvc_path(target.path)
  
    def get_intermediate_dir(self, target):
      if isinstance(target, gen_base.TargetSWIG):
        return msvc_path_join(msvc_path(target.path), target.name)
      else:
        return self.get_output_dir(target)
  
    def get_def_file(self, target):
      if isinstance(target, gen_base.TargetLib) and target.msvc_export:
        return target.name + ".def"
      return None
  
    def gen_proj_names(self, install_targets):
      "Generate project file names for the targets"
      # Generate project file names for the targets: replace dashes with
      # underscores and replace *-test with test_* (so that the test
      # programs are visually separare from the rest of the projects)
      for target in install_targets:
        if target.msvc_name:
          target.proj_name = target.msvc_name
          continue
  
        name = target.name
        pos = name.find('-test')
        if pos >= 0:
          proj_name = 'test_' + name[:pos].replace('-', '_')
        elif isinstance(target, gen_base.TargetSWIG):
          proj_name = 'swig_' + name.replace('-', '_')
        else:
          proj_name = name.replace('-', '_')
        target.proj_name = proj_name
  
    def get_external_project(self, target, proj_ext):
      if not ((isinstance(target, gen_base.TargetLinked)
               or isinstance(target, gen_base.TargetI18N))
              and target.external_project):
        return None
  
      if target.external_project.find('/') != -1:
        path = target.external_project
      else:
        path = os.path.join(self.projfilesdir, target.external_project)
  
      return "%s.%s" % (gen_base.native_path(path), proj_ext)
  
    def adjust_win_depends(self, target, name):
      "Handle special dependencies if needed"
  
      if name == '__CONFIG__':
        depends = []
      else:
        depends = self.sections['__CONFIG__'].get_dep_targets(target)
  
      depends.extend(self.get_win_depends(target, FILTER_PROJECTS))
  
      # Make the default target generate the .mo files, too
      if self.enable_nls and name == '__ALL__':
        depends.extend(self.sections['locale'].get_targets())
  
      # To set the correct build order of the JavaHL targets, the javahl-javah
      # and libsvnjavahl targets are defined with extra dependencies in build.conf
      # like this:
      # add-deps = $(javahl_javah_DEPS) $(javahl_java_DEPS)
      #
      # This section parses those dependencies and adds them to the dependency list
      # for this target.
      if name.startswith('javahl') or name == 'libsvnjavahl':
        for dep in re.findall('\$\(([^\)]*)_DEPS\)', target.add_deps):
          dep = dep.replace('_', '-')
          depends.extend(self.sections[dep].get_targets())
  
      return depends
  
    def get_win_depends(self, target, mode):
      """Return the list of dependencies for target"""
  
      dep_dict = {}
  
      if mode == FILTER_EXTERNALLIBS:
        self.get_externallib_depends(target, dep_dict)
      elif isinstance(target, gen_base.TargetLib) and target.msvc_static:
        self.get_static_win_depends(target, dep_dict)
      else:
        self.get_linked_win_depends(target, dep_dict)
  
      deps = []
  
      if mode == FILTER_PROJECTS:
        for dep, (is_proj, is_lib, is_static) in dep_dict.items():
          if is_proj:
            deps.append(dep)
      elif mode == FILTER_LIBS or mode == FILTER_EXTERNALLIBS:
        for dep, (is_proj, is_lib, is_static) in dep_dict.items():
          if is_static or (is_lib and not is_proj):
            # Filter explicit msvc libraries of optional dependencies
            if (dep.name in self._libraries
                or dep.name not in self._optional_libraries):
              deps.append(dep)
      else:
        raise NotImplementedError
  
      deps.sort(key = lambda d: d.name)
      return deps
  
    def get_direct_depends(self, target):
      """Read target dependencies from graph
      return value is list of (dependency, (is_project, is_lib, is_static)) tuples
      """
      deps = []
  
      for dep in self.graph.get_sources(gen_base.DT_LINK, target.name):
        if not isinstance(dep, gen_base.Target):
          continue
  
        is_project = hasattr(dep, 'proj_name')
        is_lib = isinstance(dep, gen_base.TargetLib)
        is_static = is_lib and dep.msvc_static
        deps.append((dep, (is_project, is_lib, is_static)))
  
      for dep in self.graph.get_sources(gen_base.DT_NONLIB, target.name):
        is_project = hasattr(dep, 'proj_name')
        is_lib = isinstance(dep, gen_base.TargetLib)
        is_static = is_lib and dep.msvc_static
        deps.append((dep, (is_project, is_lib, is_static)))
  
      return deps
  
    def get_static_win_depends(self, target, deps):
      """Find project dependencies for a static library project"""
      for dep, dep_kind in self.get_direct_depends(target):
        is_proj, is_lib, is_static = dep_kind
  
        # recurse for projectless targets
        if not is_proj:
          self.get_static_win_depends(dep, deps)
  
        # Only add project dependencies on non-library projects. If we added
        # project dependencies on libraries, MSVC would copy those libraries
        # into the static archive. This would waste space and lead to linker
        # warnings about multiply defined symbols. Instead, the library
        # dependencies get added to any DLLs or EXEs that depend on this static
        # library (see get_linked_win_depends() implementation).
        if not is_lib:
          deps[dep] = dep_kind
  
        # a static library can depend on another library through a fake project
        elif dep.msvc_fake:
          deps[dep.msvc_fake] = dep_kind
  
    def get_linked_win_depends(self, target, deps, static_recurse=0):
      """Find project dependencies for a DLL or EXE project"""
  
      direct_deps = self.get_direct_depends(target)
      for dep, dep_kind in direct_deps:
        is_proj, is_lib, is_static = dep_kind
  
        # add all top level dependencies
        if not static_recurse or is_lib:
          # We need to guard against linking both a static and a dynamic library
          # into a project (this is mainly a concern for tests). To do this, for
          # every dll dependency we first check to see if its corresponding
          # static library is already in the list of dependencies. If it is,
          # we don't add the dll to the list.
          if is_lib and dep.msvc_export:
            static_dep = self.graph.get_sources(gen_base.DT_LINK, dep.name)[0]
            if static_dep in deps:
              continue
          deps[dep] = dep_kind
  
      # add any libraries that static library dependencies depend on
      for dep, dep_kind in direct_deps:
        is_proj, is_lib, is_static = dep_kind
  
        # recurse for projectless dependencies
        if not is_proj:
          self.get_linked_win_depends(dep, deps, 0)
  
        # also recurse into static library dependencies
        elif is_static:
          self.get_linked_win_depends(dep, deps, 1)
  
        # and recurse over the external library dependencies for swig libraries,
        # to include the language runtime
        elif isinstance(dep, gen_base.TargetSWIGLib):
          self.get_externallib_depends(dep, deps)
  
    def get_externallib_depends(self, target, deps):
      """Find externallib dependencies for a project"""
  
      direct_deps = self.get_direct_depends(target)
      for dep, dep_kind in direct_deps:
        self.get_externallib_depends(dep, deps)
  
        if isinstance(target, gen_base.TargetLinked) and dep.external_lib:
          deps[dep] = dep_kind
  
    def get_win_defines(self, target, cfg):
      "Return the list of defines for target"
  
      fakedefines = ["WIN32","_WINDOWS","alloca=_alloca",
                     "_CRT_SECURE_NO_DEPRECATE=",
                     "_CRT_NONSTDC_NO_DEPRECATE=",
                     "_CRT_SECURE_NO_WARNINGS="]
  
      if cfg == 'Debug':
        fakedefines.extend(["_DEBUG","SVN_DEBUG"])
      elif cfg == 'Release':
        fakedefines.append("NDEBUG")
  
      if isinstance(target, gen_base.TargetApacheMod):
        if target.name == 'mod_dav_svn':
          fakedefines.extend(["AP_DECLARE_EXPORT"])
  
      if self.cpp_defines:
        fakedefines.extend(self.cpp_defines)
  
      if isinstance(target, gen_base.TargetSWIG):
        fakedefines.append("SWIG_GLOBAL")
  
      for dep in self.get_win_depends(target, FILTER_EXTERNALLIBS):
        if dep.external_lib:
          for elib in re.findall('\$\(SVN_([^\)]*)_LIBS\)', dep.external_lib):
            external_lib = elib.lower()
  
          if external_lib in self._libraries:
            lib = self._libraries[external_lib]
  
            if lib.defines:
              fakedefines.extend(lib.defines)
  
      # check if they wanted nls
      if self.enable_nls:
        fakedefines.append("ENABLE_NLS")
  
      if target.name.endswith('svn_subr'):
        fakedefines.append("SVN_USE_WIN32_CRASHHANDLER")
  
      return fakedefines
  
    def get_win_includes(self, target, cfg='Release'):
      "Return the list of include directories for target"
  
      fakeincludes = [ "subversion/include" ]
  
      for dep in self.get_win_depends(target, FILTER_EXTERNALLIBS):
        if dep.external_lib:
          for elib in re.findall('\$\(SVN_([^\)]*)_LIBS\)', dep.external_lib):
            external_lib = elib.lower()
  
          if external_lib in self._libraries:
            lib = self._libraries[external_lib]
  
            fakeincludes.extend(lib.include_dirs)
  
      if (isinstance(target, gen_base.TargetSWIG)
            or isinstance(target, gen_base.TargetSWIGLib)):
        util_includes = "subversion/bindings/swig/%s/libsvn_swig_%s" \
                        % (target.lang,
                           gen_base.lang_utillib_suffix[target.lang])
        fakeincludes.append(util_includes)
  
      if (isinstance(target, gen_base.TargetSWIG)
          or isinstance(target, gen_base.TargetSWIGLib)):
  
        # Projects aren't generated unless we have swig
        assert self.swig_libdir
  
        if target.lang == "perl" and self.swig_version >= (1, 3, 28):
          # At least swigwin 1.3.38+ uses perl5 as directory name.
          lang_subdir = 'perl5'
        else:
          lang_subdir = target.lang
  
        # After the language specific includes include the generic libdir,
        # to allow overriding a generic with a per language include
        fakeincludes.append(os.path.join(self.swig_libdir, lang_subdir))
        fakeincludes.append(self.swig_libdir)
  
      if 'cxxhl' in target.name:
        fakeincludes.append("subversion/bindings/cxxhl/include")
  
      return gen_base.unique(map(self.apath, fakeincludes))
  
    def get_win_lib_dirs(self, target, cfg):
      "Return the list of library directories for target"
  
      debug = (cfg == 'Debug')
  
      if not isinstance(target, gen_base.TargetLinked):
        return []
  
      if isinstance(target, gen_base.TargetLib) and target.msvc_static:
        return []
  
      fakelibdirs = []
  
      # When nls is enabled, all our projects use it directly via the _() macro,
      # even though only libsvn_subr references it in build.conf
      if self.enable_nls:
        lib = self._libraries['intl']
        if debug and lib.debug_lib_dir:
          fakelibdirs.append(lib.debug_lib_dir)
        else:
          fakelibdirs.append(lib.lib_dir)
  
      if (isinstance(target, gen_base.TargetSWIG)
            or isinstance(target, gen_base.TargetSWIGLib)):
        if target.lang in self._libraries:
          lib = self._libraries[target.lang]
  
          if debug and lib.debug_lib_dir:
            fakelibdirs.append(lib.debug_lib_dir)
          elif lib.lib_dir:
            fakelibdirs.append(lib.lib_dir)
  
      for dep in self.get_win_depends(target, FILTER_LIBS):
        if dep.external_lib:
          for elib in re.findall('\$\(SVN_([^\)]*)_LIBS\)', dep.external_lib):
            external_lib = elib.lower()
  
            if external_lib not in self._libraries:
              continue
  
            lib = self._libraries[external_lib]
  
            if debug and lib.debug_lib_dir:
              lib_dir = self.apath(lib.debug_lib_dir)
            elif lib.lib_dir:
              lib_dir = self.apath(lib.lib_dir)
            else:
              continue # Dependency without library (E.g. JDK)
  
            fakelibdirs.append(lib_dir)
  
      return gen_base.unique(fakelibdirs)
  
    def get_win_libs(self, target, cfg):
      "Return the list of external libraries needed for target"
  
      debug = (cfg == 'Debug')
  
      if not isinstance(target, gen_base.TargetLinked):
        return []
  
      if isinstance(target, gen_base.TargetLib) and target.msvc_static:
        return []
  
      nondeplibs = target.msvc_libs[:]
  
      # When nls is enabled, all our projects use it directly via the _() macro,
      # even though only libsvn_subr references it in build.conf
      if self.enable_nls:
        lib = self._libraries['intl']
        if debug and lib.debug_lib_name:
          nondeplibs.append(lib.debug_lib_name)
        else:
          nondeplibs.append(lib.lib_name)
  
      if (isinstance(target, gen_base.TargetSWIG)
            or isinstance(target, gen_base.TargetSWIGLib)):
        if target.lang in self._libraries:
          lib = self._libraries[target.lang]
          if debug and lib.debug_lib_name:
            nondeplibs.append(lib.debug_lib_name)
          elif lib.lib_name:
            nondeplibs.append(lib.lib_name)
  
      for dep in self.get_win_depends(target, FILTER_LIBS):
        nondeplibs.extend(dep.msvc_libs)
  
        if dep.external_lib:
          for elib in re.findall('\$\(SVN_([^\)]*)_LIBS\)', dep.external_lib):
  
            external_lib = elib.lower()
  
            if external_lib not in self._libraries:
              if external_lib not in self._optional_libraries:
                print('Warning: Using undeclared dependency \'$(SVN_%s_LIBS)\'.'
                      % (elib,))
              continue
  
            lib = self._libraries[external_lib]
  
            if debug:
              nondeplibs.append(lib.debug_lib_name)
            else:
              nondeplibs.append(lib.lib_name)
  
      return gen_base.unique(nondeplibs)
  
    def get_win_sources(self, target, reldir_prefix=''):
      "Return the list of source files that need to be compliled for target"
  
      sources = { }
  
      for obj in self.graph.get_sources(gen_base.DT_LINK, target.name):
        if isinstance(obj, gen_base.Target):
          continue
  
        for src in self.graph.get_sources(gen_base.DT_OBJECT, obj):
          if isinstance(src, gen_base.SourceFile):
            if reldir_prefix:
              if src.reldir:
                reldir = reldir_prefix + '\\' + src.reldir
              else:
                reldir = reldir_prefix
            else:
              reldir = src.reldir
          else:
            reldir = ''
          sources[src] = src, obj, reldir
  
      return list(sources.values())
  
    def get_win_forced_includes(self, target, cfg):
      """Return a list of include files that need to be included before any
         other header in every c/c++ file"""
  
      fakeincludes = []
  
      for dep in self.get_win_depends(target, FILTER_EXTERNALLIBS):
        if dep.external_lib:
          for elib in re.findall('\$\(SVN_([^\)]*)_LIBS\)', dep.external_lib):
            external_lib = elib.lower()
  
          if external_lib in self._libraries:
            lib = self._libraries[external_lib]
  
            fakeincludes.extend(lib.forced_includes)
  
      return gen_base.unique(fakeincludes)
  
    def write_with_template(self, fname, tname, data):
      fout = StringIO()
  
      template = ezt.Template(compress_whitespace = 0)
      template.parse_file(os.path.join('build', 'generator', tname))
      template.generate(fout, data)
      self.write_file_if_changed(fname, fout.getvalue())
  
    def move_proj_file(self, path, name, params=()):
      ### Move our slightly templatized pre-built project files into place --
      ### these projects include zlib, serf, locale, config, etc.
  
      dest_file = os.path.join(path, name)
      source_template = os.path.join('templates', name + '.ezt')
      data = {
        'version' : self.vcproj_version,
        'configs' : self.configs,
        'platforms' : self.platforms,
        'toolset_version' : 'v' + self.vcproj_version.replace('.',''),
        }
      for key, val in params:
        data[key] = val
      self.write_with_template(dest_file, source_template, data)
  
    def write(self):
      "Override me when creating a new project type"
  
      raise NotImplementedError
  
  class ProjectItem:
    "A generic item class for holding sources info, config info, etc for a project"
    def __init__(self, **kw):
      self.ignored = None
      vars(self).update(kw)
  
  # ============================================================================
  
  FILTER_LIBS = 1
  FILTER_PROJECTS = 2
  FILTER_EXTERNALLIBS = 3
  
  class POFile:
    "Item class for holding po file info"
    def __init__(self, base):
      self.po = base + '.po'
      self.mo = base + '.mo'
  
  # MSVC paths always use backslashes regardless of current platform
  def msvc_path(path):
    """Convert a build path to an msvc path"""
    return path.replace('/', '\\')
  
  def msvc_path_join(*path_parts):
    """Join path components into an msvc path"""
    return '\\'.join(path_parts)
  