Files
harbour-core/harbour/contrib/make.hbs
Viktor Szakats bf4f1b0679 2010-07-14 22:13 UTC+0200 Viktor Szakats (harbour.01 syenar.hu)
* include/hbextern.ch
  * src/rtl/hbdoc.prg
    + Added __HBDOC_LOADHBD( <cFileName> ) -> <hbdoc> | NIL
    + Added __HBDOC_SAVEHBD( <cFileName>, <hbdoc> ) -> <lSuccess>

  - doc/en/Makefile
  * doc/Makefile
  * package/mpkg_win.nsi
  * package/winuni/mpkg_win_uni_extra_copy.bat
  * package/winuni/mpkg_win_uni.nsi
    - Don't install doc sources.

  * config/postinst.hbs
    + Will now build .hbd files from doc sources at 'install' phase.
      .hbd files will be installed in HB_DOC_INSTALL path, which
      is /doc by default.
    ; NOTE: Apps like hbide can now use __HBDOC_LOADHBD() to load
            these .hbd files.
    ; NOTE: We may add other doc generators to hbrtl, plus we may
            further enhance .hbd format to be more easily usable
            for client apps (and f.e. to consume less disk space).
    ; TODO: Config HB_DOC_INSTALL also on *nix systems.

  * contrib/gtwvg/wvgwing.c
    ! Fixes after previous commit.
      Patch by Tamas Tevesz.

  * contrib/make.hbs
    * Minor.
2010-07-14 20:14:17 +00:00

652 lines
20 KiB
Handlebars
Executable File

#!/usr/bin/hbrun --hb:gtcgi
/*
* $Id$
*/
/*
* Contrib build starter script.
*
* Copyright 2010 Viktor Szakats (harbour.01 syenar.hu)
* See COPYING for licensing terms.
*/
/* TODO:
1. error handling / reporting / feedback
2. clean this source to avoid redundancy
3. solve stdalone install and HB_BUILD_CONTRIB_DLL support
4. hbmk2 location detection in standalone mode
(move hbmk2 to core lib?)
5. gnumake mode: automatically decide about build order,
based on dependencies (implibs -> libs[TODO!] -> exes)
6. HB_CONTRIBLIBS -> HB_CONTRIBS (not only libs anymore)
7. Move in external libs to contrib area? (bz2, minizip, sqlite3)
8. Pull the list of .hbp files from an external file
(so it can be used in stdalone mode, and the script can be
moved to bin and used in more generic way f.e. for examples
and local projects)
*/
#pragma warninglevel=3
/* TOFIX: Ugly hack to avoid #include "directry.ch" */
#define F_NAME 1 /* File name */
#define F_ATTR 5 /* File attribute */
#define _ACT_EXIT 0
#define _ACT_CLEAN 1
#define _ACT_INC 2
#define _ACT_INC_INST 3
#define _ACT_INC_REBUILD_INST 4
STATIC s_cBase
STATIC s_cHome
STATIC s_cRoot
STATIC s_lTest
STATIC s_lMigr
PROCEDURE Main( ... )
s_cBase := ""
s_cHome := StrTran( hb_DirBase(), hb_ps(), "/" )
s_cRoot := s_cHome + "../"
s_lMigr := "migr" $ hb_cmdLine()
IF s_lMigr
hb_setenv( "_HB_BUILD_MIGR", "yes" )
ENDIF
/* Making sure that user settings do not interfere with the std build process. */
hb_setenv( "HBMK_OPTIONS" )
IF Empty( GetEnv( "HB_HOST_BIN_DIR" ) )
StandAlone( ... )
ELSE
GNUMake( ... )
ENDIF
RETURN
/*
Workflow translation for standalone operation:
GNU Make parameter nAction hbmk2 options
-- -------------- -------------- ---------------------- -------------------------
#1 clean clean _ACT_CLEAN -clean
#2 install install _ACT_INC_INST -inc -instpath=
#3 clean install clean install _ACT_INC_REBUILD_INST -inc -instpath= -rebuild
#4 _ACT_INC -inc
*/
PROCEDURE StandAlone( ... )
LOCAL aParams
LOCAL aLibs
LOCAL aFile
LOCAL cType
LOCAL cProject
LOCAL cBaseOptions
LOCAL cOptionsPre
LOCAL cOptionsPost
LOCAL nAction
LOCAL tmp
s_lTest := .F.
aLibs := {}
FOR EACH aFile IN Directory( "*.hbi" )
AAdd( aLibs, aFile[ F_NAME ] )
NEXT
FOR EACH aFile IN Directory( "*.hbp" )
AAdd( aLibs, aFile[ F_NAME ] )
NEXT
IF Empty( aLibs )
RETURN
ENDIF
aParams := hb_AParams()
DO CASE
CASE AScan( aParams, "clean" ) > 0 .AND. ;
AScan( aParams, "install" ) > 0 .AND. ;
AScan( aParams, "install" ) > AScan( aParams, "clean" )
nAction := _ACT_INC_REBUILD_INST
CASE AScan( aParams, "clean" ) > 0
nAction := _ACT_CLEAN
CASE AScan( aParams, "install" ) > 0
nAction := _ACT_INC_INST
OTHERWISE
nAction := _ACT_INC
ENDCASE
IF nAction == _ACT_EXIT
ErrorLevel( 0 )
RETURN
ENDIF
SetCancel( .F. )
/* Converting build options to hbmk2 options */
cBaseOptions := ""
FOR EACH tmp IN aParams
IF !( tmp $ "install|clean" )
cBaseOptions += " " + tmp
ENDIF
NEXT
FOR EACH cProject IN aLibs
FOR EACH tmp IN call_hbmk2_dept( s_cRoot + "bin" + hb_ps(), s_cBase + cProject, @cType )
OutStd( cProject + " depends on: " + tmp + hb_eol() )
NEXT
OutStd( cProject + " type: " + cType + hb_eol() )
cType := get_hbmk2_project_type( s_cBase + cProject )
cProject := StrTran( cProject, "\", "/" )
cOptionsPre := cBaseOptions
cOptionsPost := ""
SWITCH cType
CASE "hblib"
CASE "hbimplib"
EXIT
CASE "hbdyn"
CASE "hbexe"
IF GetEnv( "HB_BUILD_SHARED" ) == "yes"
cOptionsPre += " -shared"
ENDIF
EXIT
ENDSWITCH
IF cType $ "hbhrb|hbppo"
LOOP
ENDIF
IF nAction == _ACT_CLEAN
cOptionsPre += " -clean"
ELSE
cOptionsPre += " -inc"
IF nAction == _ACT_INC_REBUILD_INST
cOptionsPre += " -rebuild"
ENDIF
ENDIF
call_hbmk2( s_cRoot + "bin" + hb_ps(), s_cBase + cProject, cOptionsPre, cOptionsPost )
NEXT
ErrorLevel( 0 )
RETURN
/*
Workflow translation from GNU Make to hbmk2:
GNU Make parameter HB_MAKECMDGOALS nAction hbmk2 options
-- -------------- ---------- ---------------- ---------------------- -------------------------
#1 clean clean clean _ACT_CLEAN -clean
#2 install install install _ACT_INC_INST -inc -instpath=
#3 clean install clean clean install _ACT_CLEAN -clean
install clean install _ACT_INC_REBUILD_INST -inc -instpath= -rebuild
#4 install clean install install clean _ACT_INC_INST -inc -instpath=
clean install clean _ACT_CLEAN -clean
#5 all _ACT_INC -inc
*/
PROCEDURE GNUMake( ... )
LOCAL aImpLibs := {;
"gtalleg/gtalleg.hbi" ,;
"hbblat/hbblat.hbi" ,;
"hbcairo/hbcairo.hbi" ,;
"hbcurl/hbcurl.hbi" ,;
"hbfbird/hbfbird.hbi" ,;
"hbfimage/hbfimage.hbi" ,;
"hbgd/hbgd.hbi" ,;
"hbhpdf/hbhpdf.hbi" ,;
"hbmysql/hbmysql.hbi" ,;
"hbpgsql/hbpgsql.hbi" ,;
"hbssl/hbssl.hbi" ,;
"rddads/rddads.hbi" ,;
"sddfb/sddfb.hbi" ,;
"sddmy/sddmy.hbi" ,;
"sddoci/sddoci.hbi" ,;
"sddpg/sddpg.hbi" }
/* external libs hosted locally */
LOCAL aLibsPass0 := {}
LOCAL aLibsPass1 := {;
"hbblink/hbblink.hbp" ,;
"hbbz2/hbbz2.hbp" ,; /* uses: bz2 (external) */
"hbclipsm/hbclipsm.hbp" ,;
"hbcomm/hbcomm.hbp" ,;
"hbct/hbct.hbp" ,;
"hbfoxpro/hbfoxpro.hbp" ,;
"hbfship/hbfship.hbp" ,;
"hbgt/hbgt.hbp" ,;
"hbmemio/hbmemio.hbp" ,;
"hbmisc/hbmisc.hbp" ,;
"hbmzip/hbmzip.hbp" ,; /* uses: minizip (external) */
"hbnetio/hbnetio.hbp" ,;
"hbnf/hbnf.hbp" ,;
"hbodbc/hbodbc.hbp" ,;
"hbsms/hbsms.hbp" ,;
"hbsqlit3/hbsqlit3.hbp" ,; /* uses: sqlite3 (external) */
"hbtpathy/hbtpathy.hbp" ,;
"hbwin/hbwin.hbp" ,;
"hbxpp/hbxpp.hbp" ,;
"rddbmcdx/hbbmcdx.hbp" }
LOCAL aLibsPass2 := {;
"gtalleg/gtalleg.hbp" ,;
"gtalleg/gtallegs.hbp" ,;
"hbblat/hbblat.hbp" ,;
"hbcairo/hbcairo.hbp" ,;
"hbcups/hbcups.hbp" ,;
"hbcurl/hbcurl.hbp" ,;
"hbcurl/hbcurls.hbp" ,;
"hbfbird/hbfbird.hbp" ,;
"hbfimage/hbfimage.hbp" ,;
"hbgd/hbgd.hbp" ,; /* uses: hbct */
"hbhpdf/hbhpdf.hbp" ,;
"hbmysql/hbmysql.hbp" ,;
"hbpgsql/hbpgsql.hbp" ,;
"hbqt/hbqt.hbp" ,;
"hbqt/hbqtcore.hbp" ,;
"hbqt/hbqtcores.hbp" ,;
"hbqt/hbqtgui.hbp" ,;
"hbqt/hbqtguis.hbp" ,;
"hbqt/hbqtnetwork.hbp" ,;
"hbqt/hbqtnetworks.hbp" ,;
"hbqt/hbqts.hbp" ,;
"hbssl/hbssl.hbp" ,;
"hbssl/hbssls.hbp" ,;
"hbxbp/hbxbp.hbp" ,; /* uses: hbqt */
"rddads/rddads.hbp" ,;
"rddsql/rddsql.hbp" ,;
"sddfb/sddfb.hbp" ,; /* uses: rddsql */
"sddmy/sddmy.hbp" ,; /* uses: rddsql */
"sddoci/sddoci.hbp" ,; /* uses: rddsql */
"sddodbc/sddodbc.hbp" ,; /* uses: rddsql */
"sddpg/sddpg.hbp" ,; /* uses: rddsql */
"sddsqlt3/sddsqlt3.hbp" } /* uses: rddsql, sqlite3 (external) */
LOCAL aLibsPass3 := {;
"gtwvg/gtwvg.hbp" ,; /* uses: hbwin */
"hbtip/hbtip.hbp" ,;
"hbtip/hbtipssl.hbp" ,; /* uses: hbssl */
"hbziparc/hbziparc.hbp" ,; /* uses: hbmzip */
"xhb/xhb.hbp" } /* uses: hbct, hbtip, hbwin */
LOCAL aUtils := {;
"hbdoc2/hbdoc2.hbp" ,;
"hbnetio/utils/hbnetio.hbp" ,; /* uses: hbnetio */
"hbide/hbide.hbp" } /* uses: hbxbp, hbqt */
LOCAL aAll
LOCAL aList
LOCAL cType
LOCAL cBinDir
LOCAL cProject
LOCAL cProjectDir
LOCAL cInstallDirVar
LOCAL cBaseOptions
LOCAL cOptionsPre
LOCAL cOptionsPost
LOCAL cFilter
LOCAL aFilter
LOCAL lFilterNegative
LOCAL aParams
LOCAL aGNUMakeParams
LOCAL tmp
LOCAL nAction
IF s_lMigr
OutStd( "! New contrib make orchestrator test run. |" + hb_cmdLine() + "|" + GetEnv( "HB_MAKECMDGOALS" ) + "|" + hb_dirBase() + "|" + CurDir() + "|" + hb_eol() )
/* testing with a limited set of contribs */
aLibsPass1 := {;
"hbblink/hbblink.hbp" ,;
"hbbz2/hbbz2.hbp" ,; /* uses: bz2 (external) */
"hbclipsm/hbclipsm.hbp" ,;
"hbnetio/hbnetio.hbp" ,;
"rddbmcdx/hbbmcdx.hbp" }
aLibsPass2 := {;
"gtalleg/gtalleg.hbp" ,;
"gtalleg/gtallegs.hbp" ,;
"hbblat/hbblat.hbp" ,;
"hbqt/hbqt.hbp" ,;
"hbqt/hbqtcore.hbp" ,;
"hbqt/hbqtcores.hbp" ,;
"hbqt/hbqtgui.hbp" ,;
"hbqt/hbqtguis.hbp" ,;
"hbqt/hbqtnetwork.hbp" ,;
"hbqt/hbqtnetworks.hbp" ,;
"hbqt/hbqts.hbp" ,;
"hbxbp/hbxbp.hbp" } /* uses: hbqt */
aLibsPass3 := {;
"gtwvg/gtwvg.hbp" } /* uses: hbwin */
aUtils := {;
"hbnetio/utils/hbnetio.hbp" }
ENDIF
/* Determine the mode of operation */
s_lTest := "test" $ hb_cmdLine()
aParams := hb_AParams()
aGNUMakeParams := hb_ATokens( Lower( GetEnv( "HB_MAKECMDGOALS" ) ) )
DO CASE
CASE AScan( aParams, "clean" ) > 0
IF AScan( aGNUMakeParams, "clean" ) > 0 .AND. ;
AScan( aGNUMakeParams, "install" ) > 0 .AND. ;
AScan( aGNUMakeParams, "install" ) > AScan( aGNUMakeParams, "clean" )
nAction := _ACT_CLEAN
ELSE
nAction := _ACT_CLEAN
ENDIF
CASE AScan( aParams, "install" ) > 0
IF AScan( aGNUMakeParams, "clean" ) > 0 .AND. ;
AScan( aGNUMakeParams, "install" ) > 0 .AND. ;
AScan( aGNUMakeParams, "install" ) > AScan( aGNUMakeParams, "clean" )
/* Use rebuild mode. This is needed because the clean phase
might not have been called previously by GNU Make, f.e.
because hbrun or hbmk2 wasn't available. -rebuild is
costless, so we do it to make sure to build cleanly.
[vszakats] */
nAction := _ACT_INC_REBUILD_INST
ELSE
nAction := _ACT_INC_INST
ENDIF
OTHERWISE
nAction := _ACT_INC
ENDCASE
IF nAction == _ACT_EXIT
ErrorLevel( 0 )
RETURN
ENDIF
/* Check if the requirements are met and if we have anything to do */
IF Empty( GetEnv( "HB_PLATFORM" ) ) .OR. ;
Empty( GetEnv( "HB_COMPILER" ) ) .OR. ;
Empty( GetEnv( "HB_HOST_BIN_DIR" ) )
ErrorLevel( 9 )
RETURN
ENDIF
cFilter := GetEnv( "HB_CONTRIBS" )
/* Compatibility */
IF Empty( cFilter )
cFilter := GetEnv( "HB_CONTRIBLIBS" )
ENDIF
IF cFilter == "no"
ErrorLevel( 0 )
RETURN
ENDIF
SetCancel( .F. )
/* Converting build options to hbmk2 options */
cBaseOptions := ""
/* Clearing envvars that may interact with hbmk2 */
/* Copy original install dirs to our own variables */
hb_setenv( "_HB_BIN_INSTALL", GetEnv( "HB_BIN_INSTALL" ) )
hb_setenv( "_HB_LIB_INSTALL", GetEnv( "HB_LIB_INSTALL" ) )
hb_setenv( "_HB_DYN_INSTALL", GetEnv( "HB_DYN_INSTALL" ) )
hb_setenv( "_HB_INC_INSTALL", GetEnv( "HB_INC_INSTALL" ) )
/* Override hbmk2 autodetection */
hb_setenv( "HB_INSTALL_PREFIX", s_cRoot )
hb_setenv( "HB_BIN_INSTALL", s_cRoot + "bin/" + GetEnv( "HB_PLATFORM" ) + "/" + GetEnv( "HB_COMPILER" ) + GetEnv( "HB_BUILD_NAME" ) )
hb_setenv( "HB_LIB_INSTALL", s_cRoot + "lib/" + GetEnv( "HB_PLATFORM" ) + "/" + GetEnv( "HB_COMPILER" ) + GetEnv( "HB_BUILD_NAME" ) )
hb_setenv( "HB_DYN_INSTALL" )
hb_setenv( "HB_INC_INSTALL", s_cRoot + "include" )
/* Parse filter */
aFilter := iif( Empty( cFilter ), {}, hb_ATokens( cFilter,, .T. ) )
IF Len( aFilter ) >= 1 .AND. aFilter[ 1 ] == "no"
hb_ADel( aFilter, 1, .T. )
lFilterNegative := .T.
ELSE
lFilterNegative := .F.
ENDIF
/* Start building */
cBinDir := GetEnv( "HB_HOST_BIN_DIR" ) + hb_ps()
aAll := {;
aImpLibs,;
aLibsPass0,;
aLibsPass1,;
aLibsPass2,;
aLibsPass3,;
aUtils,;
hb_ATokens( GetEnv( "HB_BUILD_ADDONS" ),, .T. ) }
OutStd( "! Calculating sorting order for contribs..." + hb_eol() )
FOR EACH aList IN aAll
FOR EACH cProject IN aList
IF ! Empty( cProject )
call_hbmk2_dept( s_cRoot + "bin" + hb_ps(), s_cBase + cProject, @cType )
ENDIF
NEXT
NEXT
OutStd( "! Building contribs..." + hb_eol() )
FOR EACH aList IN aAll
FOR EACH cProject IN aList
IF ! Empty( cProject )
cProject := StrTran( cProject, "\", "/" )
IF ( tmp := At( "/", cProject ) ) > 0
cProjectDir := Left( cProject, tmp - 1 )
ELSE
cProjectDir := ""
ENDIF
IF Empty( aFilter ) .OR. ;
iif( lFilterNegative,;
AScan( aFilter, {| tmp | tmp == cProjectDir } ) == 0,;
AScan( aFilter, {| tmp | tmp == cProjectDir } ) > 0 )
cOptionsPre := cBaseOptions
cOptionsPost := ""
cType := get_hbmk2_project_type( s_cBase + cProject )
SWITCH cType
CASE "hblib"
CASE "hbimplib"
cInstallDirVar := "_HB_LIB_INSTALL"
EXIT
CASE "hbdyn"
cInstallDirVar := "_HB_DYN_INSTALL"
IF GetEnv( "HB_BUILD_SHARED" ) == "yes"
cOptionsPre += " -shared"
ENDIF
EXIT
CASE "hbexe"
cInstallDirVar := "_HB_BIN_INSTALL"
IF GetEnv( "HB_BUILD_SHARED" ) == "yes"
cOptionsPre += " -shared"
ENDIF
EXIT
ENDSWITCH
IF cType $ "hbhrb|hbppo"
LOOP
ENDIF
IF GetEnv( "HB_BUILD_PARTS" ) == "lib" .AND. ;
!( cType $ "hbimplib|hblib" )
LOOP
ENDIF
IF nAction == _ACT_CLEAN
cOptionsPre += " -clean"
ELSE
cOptionsPre += " -inc"
IF nAction == _ACT_INC_REBUILD_INST
cOptionsPre += " -rebuild"
ENDIF
IF nAction == _ACT_INC_INST .OR. ;
nAction == _ACT_INC_REBUILD_INST
IF ! Empty( GetEnv( cInstallDirVar ) ) .AND. ;
( ! ( cType == "hbimplib" ) .OR. GetEnv( "HB_BUILD_IMPLIB" ) == "yes" )
cOptionsPre += " -instpath=${" + cInstallDirVar + "}/"
IF cType == "hblib"
cOptionsPre += " -instpath=inc:${_HB_INC_INSTALL}/"
ENDIF
ENDIF
ENDIF
ENDIF
call_hbmk2( cBinDir, s_cBase + cProject, cOptionsPre, cOptionsPost )
/* Highly experimental */
IF cType == "hblib" .AND. GetEnv( "HB_BUILD_CONTRIB_DLL" ) == "yes" .AND. ;
!( GetEnv( "HB_BUILD_PARTS" ) == "lib" ) .AND. ;
hb_FileExists( FNameExtSet( s_cBase + cProject, ".hbc" ) )
cInstallDirVar := "_HB_DYN_INSTALL"
cOptionsPre := cBaseOptions
/* TOFIX: Add -implib option. It collides with static lib so needs to be resolved. */
cOptionsPost := " -hbdyn -nohblib- " + FNameExtSet( cProject, ".hbc" )
IF nAction == _ACT_CLEAN
cOptionsPre += " -clean"
ELSE
cOptionsPre += " -inc"
IF nAction == _ACT_INC_REBUILD_INST
cOptionsPre += " -rebuild"
ENDIF
IF nAction == _ACT_INC_INST .OR. ;
nAction == _ACT_INC_REBUILD_INST
IF ! Empty( GetEnv( cInstallDirVar ) )
cOptionsPre += " -instpath=${" + cInstallDirVar + "}/"
ENDIF
ENDIF
ENDIF
call_hbmk2( cBinDir, s_cBase + cProject, cOptionsPre, cOptionsPost )
ENDIF
ELSE
/* OutStd( hb_StrFormat( "! project '%1$s' skipped", cProject ) + hb_eol() ) */
ENDIF
ENDIF
NEXT
NEXT
ErrorLevel( 0 )
RETURN
STATIC FUNCTION call_hbmk2_dept( cBinDir, cProject, /* @ */ cType )
LOCAL tmp, tmp1
LOCAL aList := {}
cType := ""
hb_processRun( PathSepToSelf( cBinDir ) + "hbmk2 --hbinfo " + cProject,, @tmp )
IF ( tmp1 := At( "targettype{{", tmp ) ) > 0
tmp := SubStr( tmp, tmp1 + Len( "targettype{{" ) )
IF ( tmp1 := At( "}}", tmp ) ) > 0
cType := Left( tmp, tmp1 - 1 )
ENDIF
ENDIF
IF ( tmp1 := At( "hbctree{{", tmp ) ) > 0
tmp := SubStr( tmp, tmp1 + Len( "hbctree{{" ) )
IF ( tmp1 := At( "}}", tmp ) ) > 0
tmp := StrTran( Left( tmp, tmp1 - 1 ), Chr( 13 ) )
FOR EACH tmp IN hb_ATokens( tmp, Chr( 10 ) )
IF ! Empty( tmp )
hb_FNameSplit( tmp,, @tmp1 )
AAdd( aList, tmp1 )
ENDIF
NEXT
ENDIF
ENDIF
RETURN aList
STATIC FUNCTION call_hbmk2( cBinDir, cProject, cOptionsPre, cOptionsPost )
LOCAL nErrorLevel
IF ! Empty( GetEnv( "HB_BUILD_NAME" ) )
cOptionsPre += " -build=" + GetEnv( "HB_BUILD_NAME" )
ENDIF
nErrorLevel := mk_hb_processRun( PathSepToSelf( cBinDir ) + "hbmk2" +;
" -lang=en -width=0 @" + s_cHome + "hbpre" + cOptionsPre +;
" " + cProject +;
" @" + s_cHome + "hbpost" +;
cOptionsPost )
IF nErrorLevel != 0
OutStd( hb_StrFormat( "! '%s' returned status: %s", cProject, hb_ntos( nErrorLevel ) ) + hb_eol() )
RETURN .F.
ENDIF
RETURN .T.
STATIC FUNCTION mk_hb_processRun( cCommand )
OutStd( cCommand + hb_eol() )
RETURN iif( s_lTest, 0, hb_processRun( cCommand ) )
STATIC FUNCTION get_hbmk2_project_type( cFileName )
LOCAL cFile := MemoRead( PathSepToSelf( cFileName ) )
IF "-hblib" $ cFile ; RETURN "hblib"
ELSEIF "-hbimplib" $ cFile ; RETURN "hbimplib"
ELSEIF "-hbdyn" $ cFile ; RETURN "hbdyn"
ELSEIF "-gh" $ cFile ; RETURN "hbhrb"
ENDIF
RETURN "hbexe"
STATIC FUNCTION FNameExtSet( cFileName, cExt )
LOCAL cDir, cName
hb_FNameSplit( cFileName, @cDir, @cName )
RETURN hb_FNameMerge( cDir, cName, cExt )
STATIC FUNCTION PathSepToSelf( cFileName )
#if defined( __PLATFORM__WINDOWS ) .OR. ;
defined( __PLATFORM__DOS ) .OR. ;
defined( __PLATFORM__OS2 )
RETURN StrTran( cFileName, "/", "\" )
#else
RETURN StrTran( cFileName, "\", "/" )
#endif