#==============================================================================
# Rules for creating MSVC project files.
# Copyright (C) 2004 by Eric Sunshine <sunshine@sunshineco.com>
#
#    This library is free software; you can redistribute it and/or modify it
#    under the terms of the GNU Library General Public License as published by
#    the Free Software Foundation; either version 2 of the License, or (at your
#    option) any later version.
#
#    This library is distributed in the hope that it will be useful, but
#    WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
#    or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Library General Public
#    License for more details.
#
#    You should have received a copy of the GNU Library General Public License
#    along with this library; if not, write to the Free Software Foundation,
#    Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#
#==============================================================================
#
# During a project file synthesis run, the property name 'projgen' in the
# container 'build' will be given a value describing which type of project
# files are being generated. For MSVC project files, the value of the 'projgen'
# property will be "msvc". When project file synthesis supports multiple tool
# versions, the 'projgen_version' property will have a value indicating the
# version of the tool for which project files are being created. For instance,
# when generating MSVC7 project files, 'projgen' will be set to "msvc" and
# 'projgen_version' will have the value "7".
#
# Jamfiles throughout the project, if they need to alter their behavior (for
# one reason or another), can invoke the Property rule to determine whether
# project files are being synthesized.  For example, to learn if project file
# synthesis is active:
#
# if [ Property build : projgen ] { ... do something ... }
#
# To take action if only a particular type of project file is being generated
# (i.e. "msvc"):
#
# if [ Property build : projgen ] = msvc { ... do something ... }
#
#==============================================================================

MSVCGEN_SUPPORTED_VERSIONS = 6 7 71 8 sn71 ;
MSVCGEN_BUILD_ROOT ?= [ ConcatDirs $(BUILDTOP) out ] ;
MSVCGEN_BUILD_TEMP ?= [ ConcatDirs $(MSVCGEN_BUILD_ROOT) msvcgen ] ;

#------------------------------------------------------------------------------
# Public rule stubs. May be implemented by msvcgen phase 1 or phase 2 or both.

## MsvcGenConfig variable [ : value ]
##   Specify additional configuration information to augment the msvcgen
##   environment.  The Jam variable named by 'variable' is set to 'value' for
##   the msvcgen run.  If 'value' is omitted, then 'variable' is cleared.
##   Invocations of MsvcGenConfig are cumulative, so variable/value tuples can
##   be provided incrementally.  It is legal to use this rule in conjunction
##   with MsvcGenConfigFile; they are not mutually exclusive.
##
##   The most common reason to invoke this rule is to provide MSVC-specific
##   fallback values for the library checks typically performed by an Autoconf
##   configure script, which the Jamfiles reference via the ExternalLibs rule.
##   Such fallbacks consist of variables named TAG.CFLAGS, TAG.LFLAGS, and
##   TAG.LIBS, where TAG represents the library's identifier exported by the
##   configure script.  Because project generation allows for finer-grained
##   control, you can also optionally set the variables TAG.DEFINES,
##   TAG.INCDIRS, and TAG.LIBDIRS.  TAG.DEFINES is a set of tokens of the form
##   "key" or "key=value".  For instance, to provide MSVC-specific fallback
##   values for the FreeType2 library, which the configure script might
##   identify as FT2, you can define variables named FT2.CFLAGS, FT2.LFLAGS,
##   FT2.LIBS, FT2.DEFINES, FT2.INCDIRS, and FT2.LIBDIRS.  The msvcgen process
##   will consult these variables when it encounters invocations of the
##   ExternalLibs rule, and apply the overrides to the generated project files.
##   If yours is a multi-platform project which conditionally defines
##   Application, Plugin, and Library targets based upon the platform, then you
##   should also define whatever additional variables or properties are needed
##   to ensure that your project's Jamfiles invoke the Application, Plugin, and
##   Library rules for modules suitable for Windows, and that they do not
##   invoke those rules for modules specific to other platforms.
rule MsvcGenConfig { MsvcGenConfig1 $(1) : $(2) ; }
rule MsvcGenConfig1 { }

## MsvcGenConfigFile file
##   Similar to MsvcGenConfig, except that the configuration is provided via a
##   file containing Jam statements.  In the common case, the file will contain
##   a series of simple Jam variable assignment statements.  May be invoked
##   multiple times to specify additional configuration files.  It is legal to
##   use this rule in conjunction with MsvcGenConfig; they are not mutually
##   exclusive.
rule MsvcGenConfigFile { MsvcGenConfigFile1 $(1) : $(2) ; }
rule MsvcGenConfigFile1 { }

## MsvcGenVariable variable [ : value ]
##   Define a variable for direct interpolation into msvcgen template files.
##   'variable' is the name of the variable to define.  'value' is the value
##   which should be assigned to 'variable'.  If 'value' is omitted, then
##   'variable' is defined as the null (empty) string.  You may invoke this
##   rule multiple times to define any number of interpolation variables.  You
##   may also invoke it multiple times for the same variable name to give the
##   variable multiple values.  In the template file, reference the variable as
##   `[% variable.0 %]' to retrieve the first element, `[% variable.1 %]' to
##   retrieve the second, and so on.  Even if you have only invoked this rule
##   once for a variable, you must still reference it in the template as
##   `[% variable.0 %]'. If the variable has multiple values, then it is common
##   to reference it in the template via a FOREACH loop, as in
##   `[% FOREACH v IN variable %]'.
rule MsvcGenVariable { MsvcGenVariable1 $(1) : $(2) ; }
rule MsvcGenVariable1 { }

## MsvcGenWorkspace name [ : accepts [ : rejects ]]
##   Specify the name of a workspace which the 'msvcgen' target should create.
##   The generated workspace file name will be prefixed by "wks".  By default,
##   a workspace contains all projects resulting from invocations of the
##   Application, Plugin, Library, and CompileGroups rules. It is possible to
##   restrict the projects placed into the workspace by providing the optional
##   'accepts' and/or 'rejects'. These are lists of Perl regular expressions
##   matched against the project names. (Project names are composed of "app",
##   "plg", "lib", and "grp" prepended to the target name given to the
##   Application, Plugin, Library, and CompileGroups rules, respectively.)
##   Reject patterns take precedence over accept patterns.  This rule must be
##   invoked before any invocations of Application, Plugin, Library, or
##   CompileGroups.  You may invoke this rule multiple times to produce
##   multiple workspaces, but you must invoke it at least once in order for the
##   'msvcgen' target to produce any output.
rule MsvcGenWorkspace { MsvcGenWorkspace1 $(1) : $(2) : $(3) ; }
rule MsvcGenWorkspace1 { }

## MsvcGenSubDir dircomponents [ : version ]
##   Invoke this rule with the location of the project file directory as a
##   whitepace-delimited set of tokens, much as you would the SubDir rule.
##   This information is used in two ways.  (1) The directory structure for the
##   generated files will be duplicated under $(MSVCGEN_BUILD_ROOT).  For
##   instance, if your project files are at "$(TOP)/proj/msvc", then the
##   generated files will be deposited at "$(MSVCGEN_BUILD_ROOT)/proj/msvc".
##   (2) The depth of the directory indicated by 'dircomponents' will be taken
##   into account when generating references to resources in your project tree.
##   For example, if your project files reside at "$(TOP)/proj/msvc", and you
##   have set "$(TOP)/resources/game.ico" as the application icon with the
##   ApplicationIconDefault or ApplicationIcon rule, then msvcgen will know
##   that game.ico can be found relative to the project file directory via the
##   path "../../resources/game.ico".  If 'version' is specified, then
##   'dircomponents' applies to generated files for that version of MSVC only.
##   If 'version' is not specified, then the path will be composed of
##   'dircomponents' and the version number.  For instance, if 'dircomponents'
##   is "$(TOP)/proj/msvc" and 'version' was not provided, then it will be
##   assumed that MSVC7 project files should reside at $(TOP)/proj/msvc7".  You
##   must invoke this rule at least once to ensure that 'msvcgen' targets are
##   made available for user invocation.
rule MsvcGenSubDir { msvcgen_path_version MsvcGenSubDir1 : $(1) : $(2) ; }
rule MsvcGenSubDir1 { }

## MsvcGenTemplateDir dircomponents
##   Invoke this rule with the location of the msvcgen template directory as a
##   whitepace-delimited set of tokens.
rule MsvcGenTemplateDir { MsvcGenTemplateDir1 $(1) ; }
rule MsvcGenTemplateDir1 { }

## MsvcExternalLibrary target [ : libs [ : mode ]]
##   Invoke this rule to link 'target' with one or more MSVC-specific external
##   libraries.  For example, 'libs' might be "wsock32.lib".  'mode' should be
##   "release" or "debug". If 'mode' is omitted, then 'libs' is used for
##   release and debug.
rule MsvcExternalLibrary { MsvcExternalLibrary1 $(1) : $(2) : $(3) ; }
rule MsvcExternalLibrary1 { }

## MsvcDefine target [ : key [ : value [ : mode ]]]
##   Invoke this rule to define an MSVC-specific preprocessor macro 'key'
##   having 'value' for 'target'.  'mode' should be "release" or "debug". If
##   'mode' is omitted, then 'key/value' is used for release and debug.
rule MsvcDefine { MsvcDefine1 $(1) : $(2) : $(3) : $(4) ; }
rule MsvcDefine1 { }

## MsvcIncDirs target : directories [ : mode ]
##   Invoke this rule to define an MSVC-specific additional include directories.
##   'mode' should be "release" or "debug". If 'mode' is omitted, then 
##   'directories' is used for release and debug.
rule MsvcIncDirs { MsvcIncDirs1 $(1) : $(2) : $(3) ; }
rule MsvcIncDirs1 { }

## MsvcCFlags target [ : cflags [ : mode ]]
##   Invoke this rule to set additional MSVC-specific compiler flags for
##   'target'.  'mode' should be "release" or "debug". If 'mode' is omitted,
##   then 'cflags' is used for release and debug.
rule MsvcCFlags { MsvcCFlags1 $(1) : $(2) : $(3) ; }
rule MsvcCFlags1 { }

## MsvcLFlags target [ : lflags [ : mode ]]
##   Invoke this rule to set additional MSVC-specific linker flags for
##   'target'.  'mode' should be "release" or "debug". If 'mode' is omitted,
##   then 'lflags' is used for release and debug.
rule MsvcLFlags { MsvcLFlags1 $(1) : $(2) : $(3) ; }
rule MsvcLFlags1 { }

## MsvcGenName target : name
##   Assign an MSVC project name to 'target'.  Normally, the project name is
##   'target' with a prefix of "app", "grp", "lib", "plg", or "wks", depending
##   upon the target's type.  The MsvcGenName rule allows you to override the
##   default name.  This rule must be invoked prior to the Application,
##   Library, Plugin, or MsvcGenWorkspace rule for 'target' in order for 'name'
##   to be accepted.  As an alternative to invoking this rule, if you wish to
##   change the default prefixes used by all targets you can use the MsvcConfig
##   rule to set the MSVC.PREFIX.appcon, MSVC.PREFIX.appgui, MSVC.PREFIX.group,
##   MSVC.PREFIX.library, MSVC.PREFIX.plugin, and MSVC.PREFIX.workspace
##   variables to whatever values you like.
rule MsvcGenName { MsvcGenName1 $(1) : $(2) ; }
rule MsvcGenName1 { }

#------------------------------------------------------------------------------
# Private utility rules. Common to all phases.

## msvcgen_path_version rule : dircomponents [ : version ]
##   If 'version' is provided, invoke 'rule' once with 'dircomponents' and
##   'version' as arguments.  If 'version' is not provided, invoke 'rule' once
##   per supported MSVC version with 'dircomponents' augmented so that
##   'version' is appended to the very last element of 'dircomponents'.  For
##   example, if 'version' is omitted, and 'dircomponents' is "TOP proj msvc",
##   then 'rule' will be invoked as "rule TOP proj msvc6 : 6", then "rule TOP
##   proj msvc7 : 7", etc.
rule msvcgen_path_version
{
  local subrule = $(1) ;
  local path = $(2) ;
  local version = $(3) ;
  if $(version)
  {
    msvcgen_version_check $(version) ;
    $(subrule) $(path) : $(version) ;
  }
  else
  {
    local v ;
    for v in $(MSVCGEN_SUPPORTED_VERSIONS)
    {
      local p = [ FReverse $(path) ] ;
      $(subrule) [ FReverse $(p[1])$(v) $(p[2-]) ] : $(v) ;
    }
  }
}

rule msvcgen_resolve_dir
{ return $($(<[1])) $(<[2-]) ; }
rule msvcgen_target_dir
{ return [ ConcatDirs $(MSVCGEN_BUILD_ROOT) $(<[2-]) ] ; }
rule msvcgen_build_dir
{ return [ ConcatDirs $(MSVCGEN_BUILD_TEMP) build$(<) ] ; }
rule msvcgen_work_dir
{ return [ ConcatDirs $(MSVCGEN_BUILD_TEMP) temp$(<) ] ; }
rule msvcgen_template_dir
{ return [ ConcatDirs [ msvcgen_resolve_dir $(<) ] ] ; }
rule msvcgen_version_check
{
  if $(<) != common && ! [ IsElem $(<) : $(MSVCGEN_SUPPORTED_VERSIONS) ]
  {
    exit "Error: `$(<)' is not a supported msvcgen version number; valid "
      "numbers are:" $(MSVCGEN_SUPPORTED_VERSIONS) ;
  }
}

rule MsvcRmTemps
{
  if $(MSVCGEN_KEEPTEMPS) != yes
  {
    RmTemps $(<) : $(>) ;
  }
}

#------------------------------------------------------------------------------
# Phase 1

if ! $(DO_MSVCGEN)
{
JAM ?= jam ;
MSVCGEN_JAMOPTIONS ?= ;
MSVCGEN_TTREEOPTIONS ?= ;
MSVCGEN_SILENT ?= no ;
MSVCGEN_VERBOSE ?= no ;
MSVCGEN_KEEPTEMPS ?= no ;
if $(MSVCGEN_SILENT) = yes
{
  MSVCGEN_JAMOPTIONS += -d0 ;
}
else
{
  MSVCGEN_TTREEOPTIONS += "--verbose" ;
  if $(MSVCGEN_VERBOSE) = yes
  {
    if $(JAMVERSION) <= 2.4
    {
      MSVCGEN_JAMOPTIONS += -d+2 ;
    }
    else
    {
      MSVCGEN_JAMOPTIONS += -d5 ;
    }
  }
}
if $(MSVCGEN_KEEPTEMPS) = yes
{
  MSVCGEN_JAMOPTIONS += "-sMSVCGEN_KEEPTEMPS=yes" ;
}

## MsvcGenConfigFile file
##   (See documentation above.)
rule MsvcGenConfigFile1
{
  MSVCGEN_CONFIG_FILES += $(<) ;
}

## MsvcGenSubDir dircomponents [ : version ]
##   (See documentation above.)
rule MsvcGenSubDir1
{
  local path = $(1) ;
  local version = $(2) ;
  if ! [ Property msvcgen : pathset$(version) ]
  {
    SetProperty msvcgen : pathset$(version) ;
    MsvcGenTarget $(path) : $(version) ;
  }
  else
  {
    Echo "Warning: MsvcGenSubDir already invoked for version $(version)" ;
  }
}

## MsvcGenTarget dircomponents : version
##   Create pseudo-targets for building and removing project files for the
##   specified version of MSVC.  'dircomponents' is interpreted as described
##   for the MsvcGenSubDir rule.  Also populates the list MSVCGEN_TARGETS with
##   names of the targets.  Clients which need to perform some pre-processing
##   prior to the actual msvcgen run may set the targets in this list to depend
##   upon client-supplied targets.
rule MsvcGenTarget
{
  local path = $(1) ;
  local version = $(2) ;
  if $(version) = common
  {
    CleanDir msvccommonclean : [ msvcgen_target_dir $(path) ] ;
    Depends msvcclean : msvccommonclean ;
  }
  else
  {
    MsvcGenUmbrella ;
    Always msvc$(version)gen ;
    NotFile msvc$(version)gen ;
    MSVC_VERSION on msvc$(version)gen = $(version) ;
    MsvcGen msvc$(version)gen ;
    Depends msvcgen : msvc$(version)gen ;
    Help msvc$(version)gen : "Create MSVC$(version) project files" ;
    MSVCGEN_TARGETS += msvc$(version)gen ;
    local commonworkdir = [ msvcgen_work_dir common ] ;
    CleanDir msvc$(version)clean :
      [ msvcgen_target_dir $(path) ]
      [ msvcgen_build_dir $(version) ]
      [ msvcgen_work_dir $(version) ] 
      $(commonworkdir) ;
    Depends msvcclean : msvc$(version)clean ;
    Help msvc$(version)clean : "Remove built MSVC$(version) project files" ;
  }
}

actions MsvcGen bind MSVCGEN_CONFIG_FILES
{
  $(JAM) $(MSVCGEN_JAMOPTIONS) \
    -sDO_MSVCGEN=yes \
    -sMSVC_VERSION=$(MSVC_VERSION) \
    -sMSVCGEN_TTREEOPTIONS='$(MSVCGEN_TTREEOPTIONS)' \
    -sMSVCGEN_CONFIG_FILES='$(MSVCGEN_CONFIG_FILES)' \
    -sTARGET.OS=WIN32 \
    msvcgen
}

## MsvcGenUmbrella
##   Create pseudo-targets for building and removing project files for all
##   version of MSVC.
rule MsvcGenUmbrella
{
  if ! [ Property msvcgen : umbrella ]
  {
    SetProperty msvcgen : umbrella ;

    Always msvcgen ;
    NotFile msvcgen ;
    Help msvcgen : "Create MSVC project files (all versions)" ;

    Always msvcclean ;
    NotFile msvcclean ;
    Help msvcclean : "Remove built MSVC project files (all versions)" ;
    CleanDir msvcclean : $(MSVCGEN_BUILD_TEMP) ;
    Depends clean : msvcclean ;
  }
}

}
else
{

#------------------------------------------------------------------------------
# Phase 2

if $(MSVCGEN_CONFIG_FILES)
{
  local f ;
  for f in $(MSVCGEN_CONFIG_FILES)
  {
    include $(f) ;
  }
}

if $(TTREE)
{
  PERL ?= perl ;
  RUN_TTREE ?= $(PERL) "\"$(TTREE)\"" ;
}
else
{
  TTREE ?= ttree ;
  RUN_TTREE ?= $(TTREE) ;
}

# Implementation note: The --strip-root directives are order-sensitive when the
# build directory is the same as the source directory, in which case TOP is "."
# and MSVCGEN_BUILD_ROOT is "./out".  If TOP incorrectly appeared first, then
# it would invalidate the following --strip-root option since TOP is a prefix
# of MSVCGEN_BUILD_ROOT.
MSVCGEN_STRIP_ROOT = $(MSVCGEN_BUILD_ROOT)/ $(TOP)/ ;

# When generating the icon file .rc, we compose a 'sed' expression out of
# MSVCGEN_BUILD_ROOT in order to ensure that the referenced icon paths are
# correct. However, we must take care to protect characters in
# MSVCGEN_BUILD_ROOT which have special meaning to 'sed'.  For instance, in
# MSVCGEN_BUILD_ROOT, if $(TOP)/ is "./", then we want it to match a literal
# period followed by a slash, not 'any character' followed by a slash, so it
# must be transformed to "\./". (Presently we take the simple-minded approach
# of protecting only "." since it arises frequently.)
MSVCGEN_SED_PROTECT = "$(SED) 's:\\.:\\\\\\.:g'" ;

MSVC_VERSION ?= 7 ;
if $(MSVC_VERSION) = 6
{
  SUFPRJ = dsp ;
  SUFWSP = dsw ;
  MSVC_SUFLIB = lii6iiib ;
  MSVC_SUFEXE = exe ;
  MSVC_FORCE_CRLF = yes ;
  MSVC_TEMPLATE_SUFFIX = 6 ;
}
else if $(MSVC_VERSION) = 7 || $(MSVC_VERSION) = 71 || $(MSVC_VERSION) = 8 || $(MSVC_VERSION) = sn71
{
  SUFPRJ = vcproj ;
  SUFWSP = sln ;
  MSVC_SUFLIB = liiixxiib ;
  MSVC_SUFEXE = exe ;

  MSVC_FORCE_CRLF = no ;
  MSVC_TEMPLATE_SUFFIX = 7 ;
  if $(MSVC_VERSION) = 7
  {
    MSVC_FORMATVERSION_PRJ = 7.00 ;
    MSVC_FORMATVERSION_WSP = 7.00 ;
  }
  else if $(MSVC_VERSION) = 71
  {
    MSVC_FORMATVERSION_PRJ = 7.10 ;
    MSVC_FORMATVERSION_WSP = 8.00 ;
  }
  else if $(MSVC_VERSION) = 8
  {
    MSVC_FORMATVERSION_PRJ = 8.00 ;
    MSVC_FORMATVERSION_WSP = 9.00 ;
  }
  else if $(MSVC_VERSION) = sn71
  {
    MSVC_FORMATVERSION_PRJ = 7.10 ;
    MSVC_FORMATVERSION_WSP = 8.00 ;
    MSVC_TEMPLATE_SUFFIX = sn71 ;
    MSVC_SUFLIB = a ;
    MSVC_SUFEXE = elf ;
  }
}
else
{
  exit "No msvcgen support for MSVC version $(MSVC_VERSION) yet!" ;
}
  
MSVC.TSUFPRJ = tproj ;
MSVC.TSUFWKP = twks ;

MSVC.DEPEND ?= ;
MSVC.DEPEND_DEBUG ?= ;
MSVC.LIBRARY ?= ;
MSVC.LIBRARY_DEBUG ?= ;
MSVC.LFLAGS ?= ;
MSVC.LFLAGS_DEBUG ?= ;
MSVC.LIBRARY_DELAY ?= ;
MSVC.CFLAGS ?= ;
MSVC.CFLAGS_DEBUG ?= ;
MSVC.DEFINES ?= ;
MSVC.DEFINES_DEBUG ?= ;
MSVC.DEPEND.appgui ?= ;
MSVC.DEPEND_DEBUG.appgui ?= ;
MSVC.LIBRARY.appgui ?= ;
MSVC.LIBRARY_DEBUG.appgui ?= ;
MSVC.LFLAGS.appgui ?= ;
MSVC.LFLAGS_DEBUG.appgui ?= ;
MSVC.CFLAGS.appgui ?= ;
MSVC.CFLAGS_DEBUG.appgui ?= ;
MSVC.DEFINES.appgui ?= ;
MSVC.DEFINES_DEBUG.appgui ?= ;
MSVC.DEPEND.appcon ?= ;
MSVC.DEPEND_DEBUG.appcon ?= ;
MSVC.LIBRARY.appcon ?= ;
MSVC.LIBRARY_DEBUG.appcon ?= ;
MSVC.LFLAGS.appcon ?= ;
MSVC.LFLAGS_DEBUG.appcon ?= ;
MSVC.CFLAGS.appcon ?= ;
MSVC.CFLAGS_DEBUG.appcon ?= ;
MSVC.DEFINES.appcon ?= ;
MSVC.DEFINES_DEBUG.appcon ?= ;
MSVC.DEPEND.plugin ?= ;
MSVC.DEPEND_DEBUG.plugin ?= ;
MSVC.LIBRARY.plugin ?= ;
MSVC.LIBRARY_DEBUG.plugin ?= ;
MSVC.LIBRARY_DELAY.plugin ?= ;
MSVC.LFLAGS.plugin ?= ;
MSVC.LFLAGS_DEBUG.plugin ?= ;
MSVC.CFLAGS.plugin ?= ;
MSVC.CFLAGS_DEBUG.plugin ?= ;
MSVC.DEFINES.plugin ?= ;
MSVC.DEFINES_DEBUG.plugin ?= ;
MSVC.DEPEND.library ?= ;
MSVC.DEPEND_DEBUG.library ?= ;
MSVC.LIBRARY.library ?= ;
MSVC.LIBRARY_DEBUG.library ?= ;
MSVC.LFLAGS.library ?= ;
MSVC.LFLAGS_DEBUG.library ?= ;
MSVC.CFLAGS.library ?= ;
MSVC.CFLAGS_DEBUG.library ?= ;
MSVC.DEFINES.library ?= ;
MSVC.DEFINES_DEBUG.library ?= ;

MSVC.PREFIX.appgui    ?= app ;
MSVC.PREFIX.appcon    ?= app ;
MSVC.PREFIX.plugin    ?= plg ;
MSVC.PREFIX.library   ?= lib ;
MSVC.PREFIX.group     ?= grp ;
MSVC.PREFIX.workspace ?= wks ;

SetProperty build : projgen : msvc ;
SetProperty build : projgen_version : $(MSVC_VERSION) ;

# MsvcAddPrefix name : type
rule MsvcAddPrefix
{
  local name = $(1) ;
  local type = $(2) ;
  local prefix = $(MSVC.PREFIX.$(type)) ;
  if $(prefix) { name = "$(prefix)$(name)" ; }
  return $(name) ;
}

#----------------------------------------------------------------------------
# Override some rules

actions GenerateWin32ManifestRc
{
    cat > $(<) << __EOF__
// This file is generated automatically.

#if !defined(PROJECTGEN_VERSION) || (PROJECTGEN_VERSION != 8)
1 24 "$(MANIFEST_NAME)"
#endif
__EOF__
}

## MsvcGenConfig variable [ : value ]
##   (See documentation above.)
rule MsvcGenConfig1
{
  $(<) = $(>) ;
}

## MsvcGenVariable variable [ : value ]
##   (See documentation above.)
rule MsvcGenVariable1
{
  MSVCGEN_VARIABLES += "$(<)|$(>)" ;
}

## MsvcGenWorkspace name [ : accepts [ : rejects ]]
##   (See documentation above.)
rule MsvcGenWorkspace1
{
  local name = $(1) ;
  local accepts = $(2) ;
  local rejects = $(3) ;
  local builddir = [ msvcgen_build_dir $(MSVC_VERSION) ] ;
  local workdir = [ msvcgen_work_dir $(MSVC_VERSION) ] ;
  local wksname = $(MSVC.NAME_OVERRIDE.$(name)) ;
  if ! $(wksname) { wksname = [ MsvcAddPrefix $(name) : workspace ] ; }

  MSVC.WORKSPACES += $(name) ;

  local respdir = $(workdir) ;
  local respfile = $(wksname:G=msvcresp:S=.resp) ;
  Always $(respfile) ;
  $(name)_WKS_RESPFILE = $(respfile) ;
  $(name)_WKS_RESPDIR = $(respdir) ;

  # Boilerplate.
  ResponseFile $(respfile) :
    "key|value"
    "accept|$(accepts)"
    "reject|$(rejects)"
    "formatversion|$(MSVC_FORMATVERSION_WSP)"
    $(MSVCGEN_VARIABLES)
    : notfile : $(respdir) ;
  MsvcRmTemps msvcgen : $(respfile) ;

  local buildfile = $(wksname:G=msvcworkspace:S=.$(MSVC.TSUFWKP)) ;
  Always $(buildfile) ;
  MakeLocate $(buildfile) : $(builddir) ;
  Includes $(buildfile) : $(respfile) ;
  MSVC_BUILD_TYPE on $(buildfile) = workspace ;
  MsvcBuildFile $(buildfile) : $(respfile) ;
  Depends msvcgenrun : $(buildfile) ;
  MsvcRmTemps msvcgen : $(buildfile) ;
}

## MsvcGenSubDir dircomponents [ : version ]
##   (See documentation above.)
rule MsvcGenSubDir1
{
  local path = $(1) ;
  local version = $(2) ;

  local relpath ;
  if ! $(path[2])
  {
    relpath = $(DOT) ;
  }
  else
  {
    local i ;
    for i in $(path[2-])
    {
      relpath += $(DOTDOT) ;
    }
  }

  SetProperty msvcgen : outdir$(version) : [ msvcgen_target_dir $(path) ] ;
  SetProperty msvcgen : relpath$(version) : $(relpath) ;

  MSVCGEN_OUTDIR_$(version) = [ Property msvcgen : outdir$(version) ] ;
}

## MsvcGenTemplateDir dircomponents
##   (See documentation above.)
rule MsvcGenTemplateDir1
{
  SetProperty msvcgen : templatedir : [ msvcgen_template_dir $(<) ] ;

  MSVCGEN_TEMPLATEDIR = [ Property msvcgen : templatedir ] ;
}

## MsvcGenName target : name
##   (See documentation above.)
rule MsvcGenName1
{
  MSVC.NAME_OVERRIDE.$(<) = $(>) ;
}

## MsvcProject target : type : target-with-ext : sources : options
##   Create a project file for 'target', which is the project's raw name.
##   'sources' is a list of files comprising the target.
rule MsvcProject
{
  local rawname = $(1) ;
  local type = $(2) ;
  local decorated = $(3) ;
  local sources = $(4) ;
  local options = $(5) ;
  local relpath = [ Property msvcgen : relpath$(MSVC_VERSION) ] ;
  local outdir = [ Property msvcgen : outdir$(MSVC_VERSION) ] ;
  local outdircommon = [ Property msvcgen : outdircommon ] ;
  if "$(outdircommon)" = ""
  {
    outdircommon = $(outdir) ;
  }
  local builddir = [ msvcgen_build_dir $(MSVC_VERSION) ] ;
  local workdir = [ msvcgen_work_dir $(MSVC_VERSION) ] ;
  local workdircommon = [ msvcgen_work_dir common ] ;

  local msvcname = $(MSVC.NAME_OVERRIDE.$(rawname)) ;
  if ! $(msvcname) { msvcname = [ MsvcAddPrefix $(rawname) : $(type) ] ; }
  $(rawname)_MSVCNAME = $(msvcname) ;
      
  local respdir = $(workdir) ;
  local respfile = $(msvcname:G=msvcresp:S=.resp) ;
  Always $(respfile) ;
  $(rawname)_PRJ_RESPFILE = $(respfile) ;
  $(rawname)_PRJ_RESPDIR = $(respdir) ;

  # Boilerplate.
  ResponseFile $(respfile) :
    "key|value"
    "formatversion|$(MSVC_FORMATVERSION_PRJ)"
    "projtype|$(type)"
    "project|$(msvcname)"
    "rawtarget|$(rawname)"
    "target|$(decorated)"
    "sourceroot|$(relpath:J=/)"
    "buildroot|$(relpath:J=/)"
    "striproot|$(MSVCGEN_STRIP_ROOT)"
    "define|$(MSVC.DEFINES)"
    "define|$(MSVC.DEFINES.$(type))"
    "definedebug|$(MSVC.DEFINES_DEBUG)"
    "definedebug|$(MSVC.DEFINES_DEBUG.$(type))"
    "cflags|$(MSVC.CFLAGS)"
    "cflags|$(MSVC.CFLAGS.$(type))"
    "cflagsdebug|$(MSVC.CFLAGS_DEBUG)"
    "cflagsdebug|$(MSVC.CFLAGS_DEBUG.$(type))"
    "lflags|$(MSVC.LFLAGS)"
    "lflags|$(MSVC.LFLAGS.$(type))"
    "lflagsdebug|$(MSVC.LFLAGS_DEBUG)"
    "lflagsdebug|$(MSVC.LFLAGS_DEBUG.$(type))"
    "library|$(MSVC.LIBRARY)"
    "library|$(MSVC.LIBRARY.$(type))"
    "librarydebug|$(MSVC.LIBRARY_DEBUG)"
    "librarydebug|$(MSVC.LIBRARY_DEBUG.$(type))"
    "librarydelay|$(MSVC.LIBRARY_DELAY)"
    "librarydelay|$(MSVC.LIBRARY_DELAY.$(type))"
    "msvcversion|$(MSVC_VERSION)"
    "static|$($(rawname)_STATIC)"
    $(MSVCGEN_VARIABLES)
    : notfile : $(respdir) ;
  MsvcRmTemps msvcgen : $(respfile) ;

  # Only include source and headers files for now.  In the future, we also
  # want to include .cfg files and any other textual resources which which the
  # user might care to read/view in the MSVC IDE.
  local i ;
  for i in $(sources)
  {
    if [ IsElem $(i:S) : .h .hpp .hxx .H .c .cc .cpp .cxx .C .m .mm .M ]
    {
      local fileinproj ;
      if $($(rawname)_STATIC) = "yes"
      {
	fileinproj = $(i) ;
      }
      else
      {
	fileinproj = $(i:R=$(SEARCH_SOURCE)) ;
      }
      ResponseFile $(respfile) : "file|$(fileinproj)" : notfile :
	$(respdir) ;
    }
  }

  # Add resource file.
  if [ IsElem $(type) : plugin appgui appcon ]
  {
    local resource = $(msvcname:S=.rc) ;
    Depends msvcgen : $(resource) ;
    MakeLocate $(resource) : $(outdircommon) ;
    SEARCH on $(resource) = $(outdircommon) ;
    NAME on $(resource) = $(rawname) ;
    $(rawname)_RCNAME = $(resource) ;
    ResponseFile $(respfile) : "file|$(resource:R=$(outdircommon))" : notfile :
      $(respdir) ;
  
    local versionrc = $(resource:S=.vrctmp) ;
    MakeLocate $(versionrc) : $(workdircommon) ;
    MakeVersionRc $(versionrc) : $(rawname) ;
    Depends $(versionrc) : $(TOP)/Jamconfig ;
    Depends $(versionrc) : $(SUBDIR)/Jamfile ;
    Win32Resource $(rawname) : $(versionrc) ;
    MsvcRmTemps $(resource) : $(versionrc) ;
    
    if $(type) = "plugin" 
    {
      local metarc = $(resource:S=.mrctmp) ;
      MakeLocate $(metarc) : $(workdircommon) ;
      SEARCH on $(metarc) = $(SEARCH_SOURCE) ;
      Depends $(metarc) : $($(rawname)_METAFILE) ;
      GenerateWin32MetadataRc $(metarc) : $($(rawname)_METAFILE) ;
      
      Win32Resource $(rawname) : $(metarc) ;
      MsvcRmTemps $(resource) : $(metarc) ;
      ResponseFile $(respfile) :
	"file|$($(rawname)_METAFILE:R=$(SEARCH_SOURCE))" : notfile :
	$(respdir) ;
    }

    if [ IsElem $(type) : appgui appcon ]
    {
      if ! [ IsElem nomanifest : $(options) ]
      {
        local manifest = $(msvcname:S=.manifest) ; 
        MakeLocate $(manifest) : $(outdircommon) ;
        MakeManifestFile $(manifest) : $(rawname) ;
        Depends $(manifest) : $(TOP)/Jamconfig ;
        Depends $(manifest) : $(SUBDIR)/Jamfile ;
        Depends msvcgen : $(manifest) ;
      
        local manifestrc = $(resource:S=.mfrctmp) ;
        MakeLocate $(manifestrc) : $(workdircommon) ;
        MANIFEST_NAME on $(manifestrc) = $(manifest) ;
        GenerateWin32ManifestRc $(manifestrc) ;
        Depends $(manifestrc) : $(manifest) ;
        if $(MSVC_VERSION) = 8
        {
          # VC8: Manifest appears in project along other files, a special build
          # tool cares about the embedding into a resource. (The resource goop
          # still has to be generated since it is shared with other VCs.)
	  ResponseFile $(respfile) : "file|$(manifest:R=$(outdircommon))" : notfile :
	    $(respdir) ;
        }
      
        Win32Resource $(rawname) : $(manifestrc) ;
        MsvcRmTemps $(resource) : $(manifestrc) ;
      }
      
      local icon = [ ApplicationIcon win32 : $(rawname) ] ;
      if ! $(icon)
      {
        local apptype = gui ;
        if $(type) = "appcon" { apptype = console ; }
        icon = [ ApplicationIconDefault win32 : $(apptype) ] ;
      }
      if $(icon)
      {
	local iconrc = $(resource:S=.iconrctmp) ;
	# RELPATH includes trailing slash (hence the "" in ConcatDirs).
	RELPATH on $(iconrc) =
	  [ ConcatDirs [ Property msvcgen : relpath$(MSVC_VERSION) ] "" ] ;
	MakeLocate $(iconrc) : $(workdircommon) ;
	Depends $(iconrc) : $(icon) ;
        Depends $(iconrc) : $(SUBDIR)/Jamfile ;
        GenerateIconRc $(iconrc) : $(icon) ;
	Win32Resource $(rawname) : $(iconrc) ;
	MsvcRmTemps $(resource) : $(iconrc) ;
      }
    }
  }
  
  local inckeys = "include" includedebug ;
  local incdirs = $(MSVC.INCDIRS_LITERAL) $(MSVC.INCDIRS_LITERAL.$(rawname)) ;
  ResponseFile $(respfile) : "$(inckeys)|$(incdirs)" : notfile : $(respdir) ;

  local incdir ;
  for incdir in $(MSVC.INCDIRS) $(MSVC.INCDIRS.$(rawname))
  {
    if $(incdir) = "."
    {
      incdir = [ ConcatDirs $(relpath) ] ;
    }
    else
    {
      incdir = [ ConcatDirs $(relpath) $(incdir) ] ;
    }
    ResponseFile $(respfile) : "$(inckeys)|$(incdir)" : notfile : $(respdir) ;
  }

  if ! [ Property msvcgen : templatedir ]
  {
    exit "Error: You must invoke MsvcGenTemplateDir for project generation." ;
  }

  local buildfile = $(msvcname:G=msvcproject:S=.$(MSVC.TSUFPRJ)) ;
  Always $(buildfile) ;
  MakeLocate $(buildfile) : $(builddir) ;
  Includes $(buildfile) : $(respfile) ;
  MSVC_BUILD_TYPE on $(buildfile) = project ;
  MsvcBuildFile $(buildfile) : $(respfile) ;
  Depends msvcgenrun : $(buildfile) ;
  MsvcRmTemps msvcgen : $(buildfile) ;

  if ! $(MSVC.WORKSPACES)
  {
    exit "You must specify at least one workspace via MsvcGenWorkspace" ;
  }

  local w ;
  for w in $(MSVC.WORKSPACES)
  {
    ResponseFile $($(w)_WKS_RESPFILE) : "project|$(msvcname)" : notfile :
      $($(w)_WKS_RESPDIR) ;
  }

  Clean msvcclean : $(target) ;
}

#----------------------------------------------------------------------------

# Ensure that the pseudo-groups exist with which the overridden Application,
# Plugin, and Library rules will register their targets. We have to do this
# here rather than at the top-level (outside of any rule invocation) because we
# need to give the client time to invoke MsvcGenWorkspace first to set up the
# workspaces into which these groups will be inserted. If we tried registering
# theses pseudo-groups at the top-level, registration would occur before the
# client has had a chance to invoke MsvcGenWorkspace, thus these pseudo-groups
# would not inhabit any workspaces.
rule RegisterWellKnownCompileGroup1
{
  local group = $(1) ;
  if $(MSVCGEN.WELL_KNOWN_GROUP_REGISTERED.$(group)) != yes
  {
    MSVCGEN.WELL_KNOWN_GROUP_REGISTERED.$(group) = yes ;
    RegisterCompileGroups $(group) ;
  }
}

rule RegisterWellKnownCompileGroup
{
  RegisterWellKnownCompileGroup1 all ;
  RegisterWellKnownCompileGroup1 $(1) ;
}

rule LinkStaticPlugins
{
  local package target plugins optplugins ;
  # Fetch the parameters
  target = $(1) ;
  plugins = $(2) ;
  optplugins = $(3) ;
  package = $(4) ;
  
  if $(package)
  {
    # External static plugins.
    # First include static plugin info
    if ! $(HAVE_STATICDEPS.$(package))
    {
      include $($(package).STATICDEPS) ;
      HAVE_STATICDEPS.$(package) = yes ;
    }
    local lflags.debug lflags.release p mode ;
    # Collect optional plugins
    for p in $(optplugins)
    {
      if [ IsElem $(p) : $(STATICPLUGINS.AVAILABLE) ]
      {
	plugins += $(p) ;
      }
    }
    # Grab flags
    for p in $(plugins)
    {
      NotFile $(p) ;
      for mode in debug release
      {
	MsvcCFlags $(target) : $(STATICPLUGIN.$(p).CFLAGS.$(mode)) : $(mode) ;
	lflags.$(mode) += $(STATICPLUGIN.$(p).LFLAGS.$(mode)) ;
      }
    }
    for mode in debug release
    {
      MsvcLFlags $(target) : 
	[ Reverse [ RemoveDups [ Reverse $(lflags.$(mode)) ] ] ] : $(mode) ;
    }
  }
  else
  {
    # link with static plugins
    LinkWith $(target) : $(STATICPLUGINS.LIBRARY) ;
    
    # Local static plugins
    local libs p ;
    # Collect optional plugins
    for p in $(optplugins)
    {
      if $($(p)_TYPE)
      {
	plugins += $(p) ;
      }
    }
    # Grab flags
    for p in $(plugins)
    {
      libs += $($(p).NEEDLIBS) ;

      ExternalLibs $(target) : $($(p).EXTERNALLIBS) ;
      for mode in debug release
      {
	MsvcExternalLibrary $(target) : $($(p)_$(mode)_EXTERNALLIBS) : $(mode) ;
	MsvcExternalLibrary $(target) : $($(p)_$(mode)_EXTERNALLIBS) : $(mode) ;
      }
    }
    LinkWith $(target) : $(libs) ;
  }

  # Generate static plugin instantiation
  local outdir = [ Property msvcgen : outdircommon ] ;
  if "$(outdir)" = ""
  {
    outdir = [ Property msvcgen : outdir$(MSVC_VERSION) ] ;
  }
  local staticuse_out ;
  if $(package)
  {
    # @@@ Prefix hardcoded
    staticuse_out = $(MSVC.PREFIX.appgui)$(<)_staticuse_$(package).cpp ; 
  }
  else
  {
    staticuse_out =
      $(MSVC.PREFIX.appgui)$(<)_staticuse.cpp ; # @@@ Prefix hardcoded
  }
  MakeLocate $(staticuse_out) : $(outdir) ;
  SEARCH on $(staticuse_out) = $(outdir) ;
  
  GenerateStaticPluginInstantiation $(staticuse_out) : $(plugins) ;
  Depends msvcgen : $(staticuse_out) ;

  # Add usefile to project
  ResponseFile $($(target)_PRJ_RESPFILE) : 
    "file|$(staticuse_out:R=$(outdir))" : notfile :
    $($(target)_PRJ_RESPDIR) ;
  Depends $(target) : $(staticuse_out) ;
}

rule Application
{
  RegisterWellKnownCompileGroup apps ;

  local projtype ;
  if [ IsElem console : $(3) ]
  {
    projtype = appcon ;
  }
  else
  {
    projtype = appgui ;
  }
  MsvcProject $(<) : $(projtype) : $(<:S=.$(MSVC_SUFEXE)) : $(>) : $(3) ;
  
  CompileGroups $(<) : all apps ;
}

rule Plugin
{
  RegisterWellKnownCompileGroup plugins ;

  $(<)_TYPE = plugin ;
  
  local metafile ;
  metafile = [ FAppendSuffix $(<) : $(SUFMETA) ] ;
  SEARCH on $(metafile) = $(SEARCH_SOURCE) ;
  $(<)_METAFILE = $(metafile) ;
  MsvcProject $(<) : plugin : $(<:S=$(MSVCGEN.PLUGIN_EXT.$(<):E=.dll)) : $(>) ;
  CompileGroups $(<) : all plugins ;

  STATICPLUGINS.SOURCES.$(<) += $(>:R=$(SEARCH_SOURCE)) ;
  STATICPLUGINS.SUBTARGETS += $(<) ;

  # Generate static variant of plugin
  #  Static registry
  local outdir = [ Property msvcgen : outdircommon ] ;
  local staticreg_out = $(MSVC.PREFIX.library)$(<)_staticreg.cpp ;
  MakeLocate $(staticreg_out) : $(outdir) ;
  SEARCH on $(staticreg_out) = $(outdir) ;
  GenerateStaticPluginRegistration $(staticreg_out) : $(metafile) ;
  Depends msvcgen : $(staticreg_out) ;
  STATICPLUGINS.SOURCES.$(<) += $(outdir)/$(staticreg_out) ;
}

rule Library
{
  RegisterWellKnownCompileGroup libs ;
  $(<)_TYPE = library ;
  MsvcProject $(<) : library : $(<:S=.$(MSVC_SUFLIB)) : $(>) ;
  CompileGroups $(<) : all libs ;
}

rule StaticPluginLibrary
{
  local name = $(<) ;
  local rejects = $(>) ;
  local t ;

  local targets = $(STATICPLUGINS.SUBTARGETS) ;
  if $(rejects)
  {
    targets = [ Filter $(targets) : $(rejects) ] ;
  }
  local sources ;
  for t in $(targets)
  {
    sources += $(STATICPLUGINS.SOURCES.$(t)) ;
  }

  $(name)_STATIC = "yes" ;
  # Set up library
  Library $(name) : $(sources) ;
  MsvcDefine $(name) : CS_STATIC_LINKED ;
  _MsvcDefine $(name) : $(STATICPLUGINS.MSVC_DEFINES) ;

  # Write out needed CFLAGS, LFLAGS
  local outdir = [ Property msvcgen : outdircommon ] ;
  if $(outdir) = ""
  {
    outdir = [ Property msvcgen : outdir$(MSVC_VERSION) ] ;
  }
  STATICPLUGINS.DEPENDENCIES = $(name)_msvc.jam ;
  MakeLocate $(STATICPLUGINS.DEPENDENCIES) : $(outdir) ;
  SEARCH on $(STATICPLUGINS.DEPENDENCIES) = $(outdir) ;
  for t in $(targets)
  {
    NotFile $(t) ;
    WriteDependencies $(STATICPLUGINS.DEPENDENCIES) : $(t) : $(name) ;
  }
  Always $(STATICPLUGINS.DEPENDENCIES) ;
  Depends msvcgen : $(STATICPLUGINS.DEPENDENCIES) ;
  
  STATICPLUGINS.LIBRARY = $(name) ;
}

# Write out CFLAGS, LFLAGS needed by a plugin
rule WriteDependencies
{
  local cflags.debug cflags.release ;
  local lflags.debug lflags.release ;
  local depfile = $(<) ;
  local plugin = $(>) ;
  local libname = $(3) ;
  local depfile_gristed = $(depfile:G=$(plugin)) ;
  MakeLocate $(depfile_gristed) : [ on $(depfile) GetVar LOCATE ] ;
  
  # "Artificially" insert static library itself
  lflags.debug += $(MSVC.PREFIX.library)$(libname)_d.$(MSVC_SUFLIB) ;
  lflags.release += $(MSVC.PREFIX.library)$(libname).$(MSVC_SUFLIB) ;
  # Collect other libs
  local libs = [ ResolveLibs $($(plugin).NEEDLIBS) ] ;
  lflags.debug += $(MSVC.PREFIX.library)$(libs)_d.$(MSVC_SUFLIB) ;
  lflags.release += $(MSVC.PREFIX.library)$(libs).$(MSVC_SUFLIB) ;
  
  # Defines
  local l ;
  for l in $($(plugin).EXTERNALLIBS)
  {
    if $($(l).DEFINES.DEBUG)
    {
      cflags.debug += "\"/D $($(l).DEFINES.DEBUG)\"" ;
      cflags.release += "\"/D $($(l).DEFINES)\"" ;
    }
    else
    {
      cflags.debug += "\"/D $($(l).DEFINES)\"" ;
      cflags.release += "\"/D $($(l).DEFINES)\"" ;
    }
  }
  # Link flags
  local mode ;
  for mode in debug release
  {
    lflags.$(mode) += $($(plugin).LFLAGS.$(mode))  ;
    lflags.$(mode) += $($(plugin)_$(mode)_EXTERNALLIBS)  ;
  }
  CFLAGS.DEBUG on $(depfile_gristed) = "$(cflags.debug)" ;
  CFLAGS.RELEASE on $(depfile_gristed) = "$(cflags.release)" ;
  lflags.debug = [ RemoveDups $(lflags.debug) ] ;
  LFLAGS.DEBUG on $(depfile_gristed) = "$(lflags.debug)" ;
  lflags.release = [ RemoveDups $(lflags.release) ] ;
  LFLAGS.RELEASE on $(depfile_gristed) = "$(lflags.release)" ;
  LIBNAME on $(depfile_gristed) = $(libname) ;
  
  if $($(depfile).FIRSTTIME) != "yes"
  {
    WriteDepFlags1 $(depfile_gristed) : $(plugin) ;
    $(depfile).FIRSTTIME = "yes" ;
  }
  else
  {
    WriteDepFlags2 $(depfile_gristed) : $(plugin) ;
  }
  Depends $(depfile) : $(depfile_gristed) ;
  Always $(depfile_gristed) ;
}

actions WriteDepFlags1 
{
  cat << EOT > $(<)
# This file is automatically generated to be used together with $(LIBNAME)
# and must be integrated by setting the correct values for the 
# HAVE_STATICDEPS.<package> and <package>.STATICDEPS via MsvcGenConfig.
# Furthermore, this file might require manual updates from the master copy 
# (usually found in the package's source repository) every now and then.
STATICPLUGINS.AVAILABLE += $(>) ;
STATICPLUGIN.$(>).CFLAGS.debug = $(CFLAGS.DEBUG) ;
STATICPLUGIN.$(>).CFLAGS.release = $(CFLAGS.RELEASE) ;
STATICPLUGIN.$(>).LFLAGS.debug = $(LFLAGS.DEBUG) ;
STATICPLUGIN.$(>).LFLAGS.release = $(LFLAGS.RELEASE) ;
EOT
}

actions WriteDepFlags2 
{
  cat << EOT >> $(<)
STATICPLUGINS.AVAILABLE += $(>) ;
STATICPLUGIN.$(>).CFLAGS.debug = $(CFLAGS.DEBUG) ;
STATICPLUGIN.$(>).CFLAGS.release = $(CFLAGS.RELEASE) ;
STATICPLUGIN.$(>).LFLAGS.debug = $(LFLAGS.DEBUG) ;
STATICPLUGIN.$(>).LFLAGS.release = $(LFLAGS.RELEASE) ;
EOT
}

rule RegisterCompileGroups
{
  local i ;
  for i in $(<)_$(MSVC.WORKSPACES)
  {
    MsvcProject $(i) : group ;
  }
}

rule CompileGroups
{
  local w ;
  for w in $(MSVC.WORKSPACES)
  {
    local i ;
    for i in $(>)_$(w)
    {
      local m  = $($(<)_MSVCNAME) ;
      if ! $(m) { m = $($(<)_$(w)_MSVCNAME) ; } # Might be a "group".
      if $(m)
      {
        ResponseFile $($(w)_WKS_RESPFILE) : "$($(i)_MSVCNAME)|$(m)" :
	  notfile : $($(w)_WKS_RESPDIR) ;
      }
    }
  }
}

rule LinkWith
{
  local libs = [ ResolveLibs $(>) ] ;
  ExternalLibs $(<) : $($(>).EXTERNALLIBS) ;
  local w ;
  for w in $(MSVC.WORKSPACES)
  {
    local l ;
    for l in $(libs)_MSVCNAME
    {
      if $($(l))
      {
	ResponseFile $($(w)_WKS_RESPFILE) : "$($(<)_MSVCNAME)|$($(l))" :
	  notfile : $($(w)_WKS_RESPDIR) ;
      }
    }
  }
  $(<).NEEDLIBS = $(libs) ;
}

rule MsvcClassifyMode
{
  local c ;
  switch $(<)
  {
    case release : c = "" ;
    case debug : c = debug ;
    case * : c = "" debug ;
  }
  return $(c) ;
}

# _MsvcRespEmit target : items : tag [ : mode [ : options ]]
#   Emit `items' to the response file for `target' using the response file tag
#   `tag'. `mode' is either "release", "debug", or empty, in which case it
#   applies to release and debug modes. If options contains "ignoremode", then
#   `mode' is not consulted.
rule _MsvcRespEmit
{
  local target = $(1) ;
  local items = $(2) ;
  local tag = $(3) ;
  local mode = $(4) ;
  local options = $(5) ;
  local modes = "" ;

  CheckOptions ignoremode : $(options) : $(target) ;
  if ! [ IsElem ignoremode ] { modes = [ MsvcClassifyMode $(mode) ] ; }

  ResponseFile $($(target)_PRJ_RESPFILE) : "$(tag)$(modes)|$(items)" :
    notfile : $($(target)_PRJ_RESPDIR) ;
}

# MsvcExternalLibrary target [ : libs [ : mode ]]
#   (See documentation above.)
rule MsvcExternalLibrary1
{
  _MsvcRespEmit $(1) : $(2) : library : $(3) ;
  local mode = $(3) ;
  mode ?= debug release ;
  $(1)_$(mode)_EXTERNALLIBS += $(2) ;
}

# MsvcDefine target [ : key [ : value [ : mode ]]]
#   (See documentation above.)
rule MsvcDefine1
{
  local target = $(1) ;
  local key = $(2) ;
  local value = $(3) ;
  local mode = $(4) ;
  if $(key)
  {
    local def ;
    if $(value)
    {
      def = "$(key)=$(value)" ;
    }
    else
    {
      def = $(key) ;
    }
    _MsvcRespEmit $(target) : $(def) : define : $(mode) ;
    STATICPLUGINS.MSVC_DEFINES += $(def) ;
  }
}

# MsvcIncDirs target : directory [ : mode ]
#   (See documentation above.)
rule MsvcIncDirs1 
{ 
  _MsvcIncDirs $(1) : $(2) : $(3) ;
}

# _MsvcDefine target : tuples [ : mode ]
#   Similar to MsvcDefine, but works with a list of `key=value' tuples. Also
#   understands `key' with no value.
rule _MsvcDefine
{
  _MsvcRespEmit $(1) : $(2) : define : $(3) ;
}

# MsvcCFLags target [ : cflags [ : mode ]]
#   (See documentation above.)
rule MsvcCFlags1
{
  _MsvcRespEmit $(1) : $(2) : cflags : $(3) ;
}

# MsvcLFlags target [ : lflags [ : mode ]]
#   (See documentation above.)
rule MsvcLFlags1
{
  local modes = [ MsvcClassifyMode $(3) ] ;
  $(target).LFLAGS.$(modes) += $(2) ;
  _MsvcRespEmit $(1) : $(2) : lflags : $(3) ;
}

# _MsvcIncDirs target : incdirs [ : mode ]
#   Helper for ExternalLibs which processes the contents of TAG.INCDIRS for
#   an external library.
rule _MsvcIncDirs
{
  _MsvcRespEmit $(1) : $(2) : include : $(3) ;
}

# _MsvcLibDirs target : libdirs [ : mode ]
#   Helper for ExternalLibs which processes the contents of TAG.LIBDIRS for
#   an external library.
rule _MsvcLibDirs
{
  _MsvcRespEmit $(1) : $(2) : libdir : $(3) ;
}

# _ExternalLibsHelperMsvc target : libtag : subrule : attribute
rule _ExternalLibsHelperMsvc
{
  local target = $(1) ;
  local lib = $(2) ;
  local rulename = $(3) ;
  local attrib = $(4) ;

  if $($(lib).$(attrib).DEBUG.$(MSVC_VERSION))
  {
    $(rulename) $(target) : $($(lib).$(attrib).DEBUG.$(MSVC_VERSION)) : debug ;
  }
  else if $($(lib).$(attrib).DEBUG)
  {
    $(rulename) $(target) : $($(lib).$(attrib).DEBUG) : debug ;
  }
  else
  {
    $(rulename) $(target) : $($(lib).$(attrib)) : debug ;
  }
  if $($(lib).$(attrib).$(MSVC_VERSION))
  {
    $(rulename) $(target) : $($(lib).$(attrib).$(MSVC_VERSION)) : release ;
  }
  else
  {
    $(rulename) $(target) : $($(lib).$(attrib)) : release ;
  }
}

rule ExternalLibs
{
  local i ;
  for i in $(>)
  {
    _ExternalLibsHelperMsvc $(<) : $(i) : MsvcCFlags : CFLAGS ;
    _ExternalLibsHelperMsvc $(<) : $(i) : MsvcLFlags : LFLAGS ;
    _ExternalLibsHelperMsvc $(<) : $(i) : MsvcExternalLibrary : LIBS ;
    _ExternalLibsHelperMsvc $(<) : $(i) : _MsvcDefine  : DEFINES ;
    _ExternalLibsHelperMsvc $(<) : $(i) : _MsvcIncDirs : INCDIRS ;
    _ExternalLibsHelperMsvc $(<) : $(i) : _MsvcLibDirs : LIBDIRS ;
    
    $(<).EXTERNALLIBS += $(i) ;
  }
}

rule IncludeDir
{
  local dir = $(1) ;
  local target = $(2) ;
  local options = $(3) ;

  if ! [ IsElem transient : $(options) ]
  {
    local tag = "INCDIRS" ;
    if [ IsElem literal : $(options) ] { tag = "INCDIRS_LITERAL" ; }

    if $(dir)
    {
      dir = [ ConcatDirs $(dir) ] ;
    }
    else
    {
      dir = "." ;
    }

    if $(target)
    {
      MSVC.$(tag).$(target) += $(dir) ;
    }
    else
    {
      MSVC.$(tag) += $(dir) ;
    }
  }
}

# Normal invocations of these two rules are made using Unix-style flags;
# possibly determined by an Autoconf configuration script.  Such flags are
# unsuitable for MSVC, so we ignore them.
rule CFlags { }
rule LFlags { }

rule Win32Resource
{
  Depends $(<) : $($(<)_RCNAME) ;
  Depends $($(<)_RCNAME) : $(>) ;
  MergeResources $($(<)_RCNAME) : $(>) ;
}

#----------------------------------------------------------------------------

actions MsvcBuildFile
{
  cat > $(<) <<EOF
[% INCLUDE '$(MSVC_BUILD_TYPE)$(MSVC_TEMPLATE_SUFFIX).tlib' respfile='$(>)' -%]
EOF
}

actions together MergeResources
{
  cat $(>) > $(<)
}

actions GenerateIconRc
{
  sedexpr=`echo 's^$(MSVCGEN_STRIP_ROOT)^^;' | $(MSVCGEN_SED_PROTECT)`
  icon=`echo "$(>)" | sed "$sedexpr"`
  echo "1 ICON \"$(RELPATH)$icon\"" > $(<)
}

#----------------------------------------------------------------------------

rule MsvcTTreeRc
{
  local target = $(<:G=ttreerc$(MSVC_VERSION)) ;
  local builddir = [ msvcgen_build_dir $(MSVC_VERSION) ] ;
  local workdir = [ msvcgen_work_dir $(MSVC_VERSION) ] ;

  WORKDIR on $(target) = $(workdir) ;
  BUILDDIR on $(target) = $(builddir) ;

  MakeLocate $(target) : $(workdir) ;
  MsvcTTreeRc1 $(target) ;
  Always $(target) ;
  Depends msvcgenrun : $(target) ;
  MsvcRmTemps msvcgen : $(target) ;

  return $(target) ;
}

actions MsvcTTreeRc1
{
  cat > $(<) <<EOF
lib = $(MSVCGEN_TEMPLATEDIR)
lib = $(WORKDIR)
src = $(BUILDDIR)
dest = $(MSVCGEN_OUTDIR_$(MSVC_VERSION))
suffix $(MSVC.TSUFPRJ)=$(SUFPRJ)
suffix $(MSVC.TSUFWKP)=$(SUFWSP)
EOF
}

#----------------------------------------------------------------------------

rule MsvcTTree
{
  NotFile $(<) ;
  Always $(<) ;
}

if $(MSVC_FORCE_CRLF) = yes
{
  actions MsvcTTree
  {
    $(RUN_TTREE) -f \$(>) $(MSVCGEN_TTREEOPTIONS) --load_perl --all
    $(PERL) -pi.bak \
      -e 'if ($ARGV ne $prev) { $prev = $ARGV; binmode(ARGVOUT) }' \
      -e 's:(?<!\015)\012:\015\012:g' \
      $(MSVCGEN_OUTDIR_$(MSVC_VERSION))/*.$(SUFPRJ) \
      $(MSVCGEN_OUTDIR_$(MSVC_VERSION))/*.$(SUFWSP)
    $(RM) $(MSVCGEN_OUTDIR_$(MSVC_VERSION))/*.bak
  }
}
else
{
  actions MsvcTTree
  {
    $(RUN_TTREE) -f \$(>) $(MSVCGEN_TTREEOPTIONS) --load_perl --all
  }
}

MsvcTTree msvcgenrun : [ MsvcTTreeRc ttree.rc ] ;

NotFile msvcgen ;
Always msvcgen ;
Depends msvcgen : msvcgenrun ;
}
