#============================================================================
# Jam configuration and actions for Win32
# Copyright (C)2003 by Matze Braun <matzebraun@users.sourceforge.net>
# 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.
#
#============================================================================
SHELL ?= "/bin/sh" ;

CMD.WINDRES ?= windres ;
# DLL-tools not used by this script, but clients might have a need for them.
CMD.DLLTOOL ?= dlltool ;
CMD.DLLWRAP ?= dllwrap ;

USE_DLLWRAP ?= no ;

NASM.FLAGS += -f win32 -DEXTERNC_UNDERSCORE ;
CCFLAGS += -pipe ;
C++FLAGS += -pipe ;
LINKLIBS += -L/usr/lib/w32api -lgdi32 -lshell32 ;
PLUGIN.LFLAGS += -mwindows ;
WINDRES.FLAGS = --use-temp-file ;

if "$(USE_DLLWRAP)" = "yes"
{
PLUGIN.LFLAGS = [ Filter $(PLUGIN.LFLAGS) : -shared ] ;
}

# Natively-built Jam 2.5 (and probably earlier) on Cygwin fails to perform
# library scanning properly and think that all object files need to be rebuilt
# (along with everything which depends upon them) when "jam install" follows
# "jam all", so disable this feature.
if $(OS) = "CYGWIN"
{
NOARSCAN = true ;
}

#----------------------------------------------------------------------------
# resource handling
# Unfortunately we can't simply specify resources on the source list, because
# Mingw/Cygwin have the limitation that they can only link 1 resource file
# in. So we have to concatenate all resourcefiles here before compiling them.

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

1 VERSIONINFO
FILEVERSION $(PACKAGE_VERSION_LIST[1]), $(PACKAGE_VERSION_LIST[2]), $(PACKAGE_VERSION_LIST[3]), $(PACKAGE_VERSION_LIST[4])
PRODUCTVERSION $(PACKAGE_VERSION_LIST[1]), $(PACKAGE_VERSION_LIST[2]), $(PACKAGE_VERSION_LIST[3]), $(PACKAGE_VERSION_LIST[4])
#ifdef CS_DEBUG
FILEFLAGS 0x1
#else
FILEFLAGS 0x0
#endif
{
  BLOCK "StringFileInfo"
  {
    BLOCK "040904E4"
    {
      VALUE "ProductName", "$(PRODUCT_NAME)"
      VALUE "ProductVersion", "$(PACKAGE_VERSION)"
      VALUE "FileVersion", "$(PACKAGE_VERSION)"
      VALUE "LegalCopyright", "$(PACKAGE_COPYRIGHT)"
      VALUE "FileDescription", "$(PRODUCT_DESCRIPTION)"
#ifdef CS_DEBUG
      VALUE "Comments", "Debug build"
#else
      VALUE "Comments", "Release build"
#endif
      VALUE "WWW", "$(PACKAGE_HOMEPAGE)"
    }
  }
}
__EOF__
}

actions GenerateWin32ManifestRc
{
    cat > $(<) << __EOF__
// This file is generated automatically.
1 24 "$(MANIFEST_NAME)"
__EOF__
}

actions GenerateWin32Manifest
{
    cat > $(<) << __EOF__
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!-- This file is generated automatically. -->
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity
__EOF__

    # Filter out non-numerical characters from the version number. Windows does
    # not like them.
    echo '$(PACKAGE_VERSION_LIST[1]).$(PACKAGE_VERSION_LIST[2]).$(PACKAGE_VERSION_LIST[3]).$(PACKAGE_VERSION_LIST[4])' | $(SED) 's/[^0123456789.]//g' | $(SED) 's/\(.*\)/    version="\1"/' >> $(<)
  
    cat >> $(<) << __EOF__
    processorArchitecture="X86"
    name="$(PRODUCT_NAME)"
    type="win32"
/>
<description>$(PRODUCT_DESCRIPTION)</description>
<dependency>
    <dependentAssembly>
	  <assemblyIdentity
	      type="win32"
	      name="Microsoft.Windows.Common-Controls"
	      version="6.0.0.0"
	      processorArchitecture="X86"
	      publicKeyToken="6595b64144ccf1df"
	      language="*"
	  />
    </dependentAssembly>
</dependency>
</assembly>
__EOF__
}

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

17485 RCDATA 
{
__EOF__

    cat $(>) | $(SED) "s:\":\"\":g" | $(SED) "s:\(.*\):  \"\1\",:g" >> $(<)

    cat >> $(<) << __EOF__
  "\0"
}
__EOF__

}

actions GenerateIconRc
{
  echo '1 ICON "$(>)"' > $(<)
}

actions together CompileResources
{
    cat $(>) | $(CMD.WINDRES) $(WINDRES.FLAGS) $(RCFLAGS) --include-dir=$(>:D) -o $(<)
}

rule CompileResources
{
    Depends $(<) : $(>) ;
}

##  Win32Resource basename : rcfiles
##    Specify .rc files for the target known as basename.  The .rc files should
##    already have SEARCH set appropriately.
rule Win32Resource
{
    local target = $($(<)_TARGET) ;
    local rcobject = [ DoObjectGrist $(<)_resource.o ] ;
    LOCATE on $(rcobject) = $(LOCATE_TARGET) ;
    SEARCH on $(rcobject) = $(LOCATE_TARGET) ;
    
    # only add 1 resource object per target
    if ! $($(<)_HASWIN32RESOURCE)
    {
	$(<)_HASWIN32RESOURCE = yes ;
	ExtraObjects $(<) : $(rcobject) ;
    }

    CompileResources $(rcobject) : $(>) ;
}

##  Win32RcFlags basename : rcfiles
##    Specify flags passed to the resource compiler when compiling resources
##    for the target known as basename.
rule Win32RcFlags
{
    local rcobject = [ DoObjectGrist $(<)_resource.o ] ;
    RCFLAGS on $(rcobject) += $(>) ;
}

if $(USE_DLLWRAP) = "yes"
{
rule GenerateExportDefs
{
  SEARCH on $(<) = $(LOCATE_TARGET) ;
  MakeLocate $(<) : $(LOCATE_TARGET) ;
  Depends $(<) : $(>) ;
}

actions GenerateExportDefs
{
  echo "EXPORTS" > $(<)
  echo "  plugin_compiler" >> $(<)
  $(SED) '/<implementation>/!d;s:[ 	]*<implementation>\(..*\)</implementation>:  \1_scfInitialize:;p;s:_scfInitialize:_scfFinalize:;p;s:_scfFinalize:_Create:' < $(>) >> $(<)
}
}

#----------------------------------------------------------------------------
# linking part

##  ConstructApplicationTarget target : options
##    Constructs the application target name (ie. foo.exe for foo)
rule ConstructApplicationTarget
{
  return $(<).exe ;
}
rule ConstructStaticLibraryTarget
{
  return lib$(<)$(SUFLIB) ;
}
rule ConstructSharedLibraryTarget
{
  return $(<)-$(COMPILER.TYPE).dll ;
}
rule ConstructSharedLibraryLinkLib
{
  return lib$(<)$(SUFLIB) ;
}
rule ConstructPluginTarget
{
  return $(<).dll ;
}

# Also invoked by msvcgen.jam.
rule MakeVersionRc
{
  # normalize version list
  local v1, v2, v3, v4 ;
  v1 = $(PACKAGE_VERSION_LIST[1]) ;
  if ! $(v1) { v1 = 0 ; }
  v2 = $(PACKAGE_VERSION_LIST[2]) ;
  if ! $(v2) { v2 = 0 ; }
  v3 = $(PACKAGE_VERSION_LIST[3]) ;
  if ! $(v3) { v3 = 0 ; }
  v4 = $(PACKAGE_VERSION_LIST[4]) ;
  if ! $(v4) { v4 = 0 ; }
  PACKAGE_VERSION_LIST on $(<) = $(v1) $(v2) $(v3) $(v4) ;
      
  local desc = [ Description $(>) ] ;
  if ! $(desc) { desc = "$(>)" ; }
  PRODUCT_DESCRIPTION on $(<) = $(desc) ;

  local name ;
  if $(PACKAGE_LONGNAME) { name = $(PACKAGE_LONGNAME) ; }
  else if $(PACKAGE_NAME) { name = $(PACKAGE_NAME) ; }
  else { name = $(>) ; }
  PRODUCT_NAME on $(<) = $(name) ;
      
  GenerateWin32VersionRc $(<) ;
}

rule MakeManifestFile
{
  # normalize version list
  local v1, v2, v3, v4 ;
  v1 = $(PACKAGE_VERSION_LIST[1]) ;
  if ! $(v1) { v1 = 0 ; }
  v2 = $(PACKAGE_VERSION_LIST[2]) ;
  if ! $(v2) { v2 = 0 ; }
  v3 = $(PACKAGE_VERSION_LIST[3]) ;
  if ! $(v3) { v3 = 0 ; }
  v4 = $(PACKAGE_VERSION_LIST[4]) ;
  if ! $(v4) { v4 = 0 ; }
  PACKAGE_VERSION_LIST on $(<) = $(v1) $(v2) $(v3) $(v4) ;
      
  local desc = [ Description $(>) ] ;
  if ! $(desc) { desc = "$(>)" ; }
  PRODUCT_DESCRIPTION on $(<) = $(desc) ;

  local name ;
  if $(PACKAGE_NAME) { name = $(PACKAGE_NAME).$(>) ; }
  else { name = $(>) ; }
  PRODUCT_NAME on $(<) = $(name) ;
      
  GenerateWin32Manifest $(<) ;
}

rule MakeManifestRc
{
  manifestfile = $(<:S=.manifest) ;
  MakeLocate $(manifestfile) : [ on $(<) GetVar LOCATE ] ;
  MakeManifestFile $(manifestfile) : $(>) ;
  MANIFEST_NAME on $(<) = $(manifestfile:G=) ;
  GenerateWin32ManifestRc $(<) ;
  Depends $(<) : $(manifestfile) ;
}

rule MakeMetadataRc
{
  local metafile = $($(>)_METAFILE) ;
  local metarc = $(<) ;
  Depends $(metarc) : $(metafile) ;
  GenerateWin32MetadataRc $(metarc) : $(metafile) ;
  Clean $(>)clean : $(metarc) ;
  Clean clean : $(metarc) ;
}

rule AppResourceWin32
{
  local options = $(2) ;

  local versionrc = [ DoObjectGrist _versionrc.rc ] ;
  LOCATE on $(versionrc) = $(LOCATE_TARGET) ;
  SEARCH on $(versionrc) = $(LOCATE_TARGET) ;
  
  MakeVersionRc $(versionrc) : $(<) ;
  Clean $(<)clean : $(versionrc) ;
  Clean clean : $(versionrc) ;
  Win32Resource $(<) : $(versionrc) ;

  if ! [ IsElem nomanifest : $(options) ]
  {
    local manifestrc = [ DoObjectGrist _manifestrc.rc ] ;
    LOCATE on $(manifestrc) = $(LOCATE_TARGET) ;
    SEARCH on $(manifestrc) = $(LOCATE_TARGET) ;
  
    MakeManifestRc $(manifestrc) : $(<) ;
    Clean $(<)clean : $(manifestrc) ;
    Clean clean : $(manifestrc) ;
    Win32Resource $(<) : $(manifestrc) ;
  }

  local icon = [ ApplicationIcon win32 : $(<) ] ;
  if ! $(icon)
  {
    local apptype = gui ;
    if [ IsElem console : $(>) ] { apptype = console ; }
    icon = [ ApplicationIconDefault win32 : $(apptype) ] ;
  }
  if $(icon)
  {
    local iconcopy = [ DoObjectGrist $(icon) ] ;
    MakeLocate $(iconcopy) : $(LOCATE_TARGET) ;
    Depends $(iconcopy) : $(icon) ;
    Copy $(iconcopy) : $(icon) ;
    Clean $(<)clean : $(iconcopy) ;
    Clean clean : $(iconcopy) ;

    local iconrc = [ DoObjectGrist $(<)_iconrc.rc ] ;
    MakeLocate $(iconrc) : $(LOCATE_TARGET) ;
    Depends $(iconrc) : $(iconcopy) ;
    GenerateIconRc $(iconrc) : $(iconcopy) ;
    Clean $(<)clean : $(iconrc) ;
    Clean clean : $(iconrc) ;

    Win32Resource $(<) : $(iconrc) ;
  }
}    

rule PluginResourceWin32
{
  local versionrc, metarc ;

  versionrc = [ DoObjectGrist $(<)_versionrc.rc ] ;
    MakeLocate $(versionrc) : $(LOCATE_TARGET) ;
  MakeVersionRc $(versionrc) : $(<) ;
  
  if $(EMBED_META) = "yes"
  {
    metarc = [ DoObjectGrist $(<)_metarc.rc ] ;
    MakeLocate $(metarc) : $(LOCATE_TARGET) ;
    MakeMetadataRc $(metarc) : $(<) ;
  }

  Clean clean : $(versionrc) $(metarc) ;
  Clean $(<)clean : $(versionrc) $(metarc) ;

  Win32Resource $(<) : $(versionrc) $(metarc) ;
}

# SystemLinkApplication target : objects : options
rule SystemLinkApplication
{
  local target = $($(<)_TARGET) ;

  Depends $(target) : $(>) ;
  LinkApplication $(target) : $(>) ;
  # setup clean rules
  Clean clean : $(target) ;
  Clean $(<)clean : $(target) ;

  AppResourceWin32 $(<) : $(3) ;
}

rule SystemInstallApplication
{
  Depends install_bin :
    [ DoInstall $(<) : $(bindir) $(2) : $(INSTALL_PROGRAM) ] ;
}

rule SystemInstallPlugin
{
  Depends install_plugin : [ DoInstall $(<) : $(plugindir) $(2) :
      $(INSTALL_PROGRAM) ] ;
}

# SystemLinkPlugin target : objects : options
rule SystemLinkPlugin
{
  local target = $($(<)_TARGET) ;

  if $(USE_DLLWRAP) = "yes"
  {
    local exportdefs = [ DoObjectGrist $(<).def ] ;
    NAME on $(exportdefs) = $(<) ;
    GenerateExportDefs $(exportdefs) : $($(<)_METAFILE) ;
    Depends $(target) : $(exportdefs) ;
    EXPORTDEFS on $(target) = $(exportdefs) ;
  }

  Depends $(target) : $(>) ;
  LinkPlugin $(target) : $(>) ;
  PluginResourceWin32 $(<) ;

  if $(EMBED_META) != "yes"
  {
    PluginMetaData $(<) : $($(<)_METAFILE) : $(3) ;
  }

  Clean clean : $(target) ;
  Clean $(<)clean : $(target) ;
}

rule MakeDllDefFile
{
  local def = $(<) ;
  local objects = $(>) ;
  
  MakeLocate $(def) : $(LOCATE.OBJECTS)/libs ;
  WriteDefHeader $(def) ;
  for i in $(objects)
  {
    local drectve = $(i).drectve ;
    MakeLocate $(drectve) : [ on $(i) GetVar LOCATE ] ;
    Depends $(drectve) : $(i) ;
    Depends $(def) : $(drectve) ;
    ExtractDrectve $(drectve) : $(i) ;
    DrectveToDef $(def) : $(drectve) ;
    Clean clean : $(drectve) ;
    Clean $(def)clean : $(drectve) ;
  }
}

rule SystemLinkSharedLibrary
{
  LFlags $(<) : $(LINKLIBS) ;
  
  # The "crystalspace" library is so massive that it blows Jam's command line
  # length limit when all object files are passed in at once to some command.
  # However, a DLL, like an executable, needs to be built with all objects at 
  # once. 
  # Problem is, since dllwrap etc. don't support response files, there is no 
  # way to pass all objects at once to it. But, a static library can be built
  # piecemeal, so we take the detour of creating a static library out of the
  # DLL objects first.
  # However, another bug hits us: on MinGW, symbols exported from objects in
  # static libraries are not automatically exported from the DLL.
  # We work this around by constructing a .DEF file with all exports of the 
  # DLL. Conveniently (somewhat), all exported symbols are into a section
  # ".drectve" of an object file by gcc. After extracting these sections and
  # some postprocessing, we finally have all .DEF entries. Phew.
  local lib = $(<).a ;
  MakeLocate $(lib) $(lib)($(>:BS)) : $(LOCATE.OBJECTS)/libs ;
  Depends $(lib) : $(lib)($(>:BS)) ;
  local i ;
  for i in $(>)
  {
    Depends $(lib)($(i:BS)) : $(i) ;
  }
  Archive $(lib) : $(>) ;
  Ranlib $(lib) ;
  #RmTemps $(lib) : $(>) ;
  
  Depends $(<) : $(lib) ;
  
  local def = $(<).def ;
  MakeDllDefFile $(def) : $(>) ;
  Depends $(<) : $(def) ;
  Clean clean : $(def) ;
  Clean $(<)clean : $(def) ;
  Depends $(<)clean : $(def)clean ;
  
  EXPORTDEFS on $(<) = $(def) ;
  IMPLIB on $(<) = $(3) ;
  Depends $(3) : $(<) ;
  LinkSharedLibrary $(<) : $(lib) ;

  Clean clean : $(target) ;
  Clean $(<)clean : $(target) ;
}

actions ExtractDrectve
{
  $(CMD.OBJCOPY) -j .drectve -O binary $(>) $(<).tmp
  echo >> $(<).tmp # Cheap trick to ensure there's always a .tmp file; 
                   # the action should not break with no .drectve - no exports
                   # is a valid scenario.
  $(CMD.STRINGS) -n 1 $(<).tmp > $(<) # Run strings since the section data may be 
                               # padded with zeroes, get rid of those.
  rm $(<).tmp
}

actions WriteDefHeader
{
  echo "EXPORTS" > $(<)
}

actions piecemeal together DrectveToDef
{
  for d in $(>)
  do
    sed -e "s/ /\\
/g" $d | sed -e "s/-export:\(.*\)/\1/g" | sed -e "s/,data/ DATA/g" >> $(<)
  done
}

actions GenerateImportLib
{
  $(CMD.DLLTOOL) -d $(>) -l $(<)
}

actions LinkApplication bind NEEDLIBS bind EXTRAOBJECTS
{
  $(CMD.LINK) -o $(<) $(>) $(EXTRAOBJECTS) $(NEEDLIBS) $(LINKLIBS)
}

if $(USE_DLLWRAP) != "yes"
{
  actions LinkPlugin bind NEEDLIBS bind EXTRAOBJECTS
  {
    $(CMD.LINK) -o $(<) $(>) $(EXTRAOBJECTS) $(NEEDLIBS) $(LINKLIBS)
  }
}
else
{
  actions LinkPlugin bind NEEDLIBS bind EXTRAOBJECTS bind EXPORTDEFS
  {
    $(CMD.DLLWRAP) --driver-name=$(CMD.LINK) --dllname $(<:B) --def=$(EXPORTDEFS) -o $(<) $(>) $(EXTRAOBJECTS) $(NEEDLIBS) $(LINKLIBS)
  }
}

actions LinkSharedLibrary bind NEEDLIBS bind EXTRAOBJECTS bind EXPORTDEFS bind IMPLIB
{
  $(CMD.DLLWRAP) --driver-name=$(CMD.LINK) --dllname $(<:B).dll --def=$(EXPORTDEFS) --implib $(IMPLIB) -o $(<) $(>) $(EXTRAOBJECTS) $(NEEDLIBS) $(LINKLIBS)
  touch $(IMPLIB) # ensure the IMPLIB is always newer than the DLL, 
                  # since the IMPLIB depends on the DLL.
}
