Files
harbour-core/harbour/contrib/make.hbs
Viktor Szakats 6b758c4f6d 2010-07-25 10:41 UTC+0200 Viktor Szakats (harbour.01 syenar.hu)
* contrib/xhb/hbcompat.ch
    + Added xhb translations for hb_eol() and hb_ps().

  * contrib/make.hbs
    - Deleted SetCancel().

  * src/debug/tbrwtext.prg
  * src/debug/debugger.prg
  * src/rtl/errsys.prg
  * src/rtl/profiler.prg
  * src/rtl/hbini.prg
  * src/rtl/treport.prg
  * src/rtl/tpersist.prg
  * src/rtl/hbi18n2.prg
  * src/rtl/ttextlin.prg
  * src/rtl/teditor.prg
  * src/rdd/usrrdd/rdds/logrdd.prg
  * tests/hbpptest/hbpptest.prg
  * tests/longstr2.prg
  * tests/cdow.prg
  * tests/output.prg
  * tests/dirtest.prg
  * tests/tstdbi.prg
  * tests/speedold.prg
  * tests/dates3.prg
  * tests/set_test.prg
  * tests/rto_get.prg
  * tests/cpinfo.prg
  * tests/speedtst.prg
  * tests/dates.prg
  * tests/set_num.prg
  * tests/rto_tb.prg
  * tests/testhtml.prg
  * tests/round.prg
  * tests/dates2.prg
  * tests/dates4.prg
  * tests/version.prg
  * tests/seconds.prg
  * tests/gtkeys.prg
  * tests/adirtest.prg
  * include/simpleio.ch
  * include/assert.ch
  * contrib/hbct/tests/tab.prg
  * contrib/hbct/tests/expomant.prg
  * contrib/xhb/traceprg.prg
  * contrib/xhb/hblog.prg
  * contrib/xhb/dumpvar.prg
  * contrib/xhb/dbgfx.prg
  * contrib/xhb/xhberr.prg
  * contrib/xhb/cstruct.prg
  * contrib/hbqt/generator/hbqtgen.prg
  * contrib/hbqt/hbqt_errorsys.prg
  * contrib/hbqt/generator2/hbqtgen2.prg
  * contrib/hbxpp/hbxpp.ch
  * contrib/hbnetio/utils/netiosrv.prg
  * contrib/hbnetio/utils/rpcdemo.hbs
  * contrib/hbnetio/utils/netiocmd.prg
  * contrib/hbpgsql/tests/dbf2pg.prg
  * contrib/hbmisc/tests/readfile.prg
  * contrib/hbmisc/tests/testhbf.prg
  * contrib/hbtip/thtml.prg
  * contrib/hbtip/client.prg
  * contrib/hbwin/tests/testsim.prg
  * contrib/hbwin/tests/testole.prg
  * contrib/hbwin/tests/testsvc.prg
  * contrib/hbssl/tests/bio.prg
  * contrib/hbssl/tests/pem.prg
  * contrib/hbide/ideedit.prg
  * contrib/hbide/idemisc.prg
  * contrib/hbide/idethemes.prg
  * contrib/hbide/idesaveload.prg
  * contrib/hbide/idedocwriter.prg
  * contrib/hbide/hbide.ch
  * contrib/hbide/ideprojmanager.prg
  * contrib/hbide/idehome.prg
  * contrib/hbide/ideshortcuts.prg
  * utils/hbformat/hbformat.prg
  * utils/hbmk2/hbmk2.prg
  * utils/hbi18n/hbi18n.prg
  * utils/hbtest/hbtest.prg
  * examples/hbextern/hbextern.prg
  * examples/uhttpd2/umain.prg
  * examples/guestbk/inifiles.prg
  * examples/guestbk/testcgi.prg
  * examples/httpsrv/uhttpd.prg
  * examples/hscript/hscript.prg
    * hb_osnewline() -> hb_eol()
      (deleting temp variables and temp pp macros used in the past
      to workaround the long typing of the old function name)
    * Some formatting along the way in tests dir.

  * src/rtl/errsys.prg
    - Deleted usage of ISNIL(). It's a quite pointless macro.

  * src/rtl/errsys.prg
  * src/rtl/listbox.prg
  * src/rtl/profiler.prg
  * src/rtl/treport.prg
  * src/rtl/tpersist.prg
  * src/rtl/tgetlist.prg
  * src/rtl/teditor.prg
    % DO CASE -> SWITCH

  * src/rtl/alert.prg
    * Replaced DEFAULT TO with simple NIL check.

  * src/compiler/gencc.c
    * Formatting of generated code.

  * contrib/hbide/ideedit.prg
  * contrib/hbide/idemisc.prg
  * contrib/hbide/idethemes.prg
  * contrib/hbide/idesaveload.prg
  * contrib/hbide/idedocwriter.prg
  * contrib/hbide/hbide.ch
  * contrib/hbide/ideprojmanager.prg
  * contrib/hbide/idehome.prg
  * contrib/hbide/ideshortcuts.prg
    * hb_osPathSeparator() (and static variable) -> hb_ps()
2010-07-25 08:45:50 +00:00

720 lines
21 KiB
Handlebars
Executable File

#!/usr/bin/hbrun --hb:gtcgi
/*
* $Id$
*/
/*
* Harbour Project source code:
* Package build orchestrator script
*
* Copyright 2010 Viktor Szakats (harbour.01 syenar.hu)
* www - http://harbour-project.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA (or visit
* their web site at http://www.gnu.org/).
*
*/
/* TODO:
1. normalize path to hbpre/hbpost to save on cmdline
2. solve HB_BUILD_CONTRIB_DLL support the .dlls are either
huge, or they don't link.
3. hbmk2 location detection in standalone mode
(or move hbmk2 to core lib?)
4. Move in external libs to contrib area? (bz2, minizip, sqlite3)
5. 'install' is ignored in stdalone mode. It would be needed to
replicate the install dir defaulting logic found in global.mk
to implement it.
6. Delete header install related logic from GNU Make system,
also delete doc/*.txt install related logic (easily replacable
with postinst.hbs logic)
7. Move some logic to hbmk2.
8. Honor paths found in --hbinfo hbctree{{}} section.
*/
#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_CLEAN 1
#define _ACT_INC 2
#define _ACT_INC_INST 3
#define _ACT_INC_REBUILD_INST 4
STATIC hActions := {;
_ACT_CLEAN => "clean" ,;
_ACT_INC => "build" ,;
_ACT_INC_INST => "build and install" ,;
_ACT_INC_REBUILD_INST => "rebuild and install" }
STATIC s_cBase
STATIC s_cHome
STATIC s_cRoot
STATIC s_lTest
STATIC s_hPackageList
PROCEDURE Main( ... )
s_cBase := ""
s_cHome := StrTran( hb_DirBase(), hb_ps(), "/" )
s_cRoot := s_cHome + "../"
s_lTest := "test" $ hb_cmdLine()
IF "migr" $ hb_cmdLine()
hb_setenv( "_HB_BUILD_MIGR", "yes" )
ENDIF
/* Load list of packages */
s_hPackageList := { => }
hb_HKeepOrder( s_hPackageList, .T. )
LoadPkgListFromFile( s_hPackageList, s_cHome + "pkglist" )
LoadPkgListFromString( s_hPackageList, GetEnv( "HB_BUILD_ADDONS" ) )
/* Build */
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 aSortedList
LOCAL cPWD
LOCAL cBinDir
LOCAL cPackage
LOCAL cMyPackage
LOCAL cOptions
LOCAL cOptionsUser
LOCAL nAction
LOCAL tmp
LOCAL tmp1
LOCAL lCustom
LOCAL aPairList
/* Processing cmdline options */
aParams := hb_AParams()
DO CASE
CASE AScanL( aParams, "clean" ) > 0 .AND. ;
AScanL( aParams, "install" ) > 0 .AND. ;
AScanL( aParams, "install" ) > AScanL( aParams, "clean" )
nAction := _ACT_INC_REBUILD_INST
CASE AScanL( aParams, "clean" ) > 0
nAction := _ACT_CLEAN
CASE AScanL( aParams, "install" ) > 0
nAction := _ACT_INC_INST
OTHERWISE
nAction := _ACT_INC
ENDCASE
cOptionsUser := ""
lCustom := .F.
FOR EACH tmp IN aParams
IF !( Lower( tmp ) == "install" ) .AND. ;
!( Lower( tmp ) == "clean" ) .AND. ;
!( Lower( tmp ) == "all" ) .AND. ;
!( Lower( tmp ) == "first" )
cOptionsUser += " " + tmp
/* If anything else is passed than options or GNU Make keywords,
consider it a custom project build, f.e. in tests */
IF !( Left( tmp, 1 ) == "-" )
lCustom := .T.
ENDIF
ENDIF
NEXT
IF ! lCustom
cPWD := hb_pwd()
/* Find out which package are we */
FOR EACH tmp IN s_hPackageList
tmp1 := hb_ps() + PathSepToSelf( tmp:__enumKey() ) + hb_ps()
IF tmp1 == Right( cPWD, Len( tmp1 ) )
cMyPackage := StrTran( tmp:__enumKey(), "\", "/" )
EXIT
ENDIF
NEXT
IF Empty( cMyPackage )
lCustom := .T.
ELSE
OutStd( hb_StrFormat( "! Package '%1$s' %2$s...", cMyPackage, hActions[ nAction ] ) + hb_eol() )
ENDIF
ENDIF
cBinDir := s_cRoot + "bin" + hb_ps()
IF lCustom
mk_hb_processRun( PathSepToSelf( cBinDir ) + "hbmk2" + cOptionsUser )
ELSE
/* Converting build options to hbmk2 options */
cOptions := ""
IF nAction == _ACT_CLEAN
cOptions += " -clean"
ELSE
cOptions += " -inc"
IF nAction == _ACT_INC_REBUILD_INST
cOptions += " -rebuild"
ENDIF
ENDIF
/* Query project information and dependencies and calculate build order */
aPairList := {}
call_hbmk2_hbinfo( cBinDir, s_cBase + s_hPackageList[ cMyPackage ][ "cFileName" ], s_hPackageList[ cMyPackage ] )
DeptLinesToDeptPairList( aPairList, cMyPackage, s_hPackageList[ cMyPackage ][ "aDept" ] )
aSortedList := TopoSort( aPairList )
/* Build the dependencies and ourselves in in sorted order */
FOR EACH cPackage IN aSortedList DESCEND
call_hbmk2( cBinDir,;
s_cBase + iif( cPackage == cMyPackage, "", s_cHome + cPackage + "/" ) + s_hPackageList[ cPackage ][ "cFileName" ],;
iif( cPackage == cMyPackage, cOptions + cOptionsUser, " -inc" ), "" )
NEXT
ENDIF
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 cBinDir
LOCAL hProject
LOCAL cProject
LOCAL cPackage
LOCAL cOptions
LOCAL cFilter
LOCAL aFilter
LOCAL lFilterNegative
LOCAL aParams
LOCAL aGNUMakeParams
LOCAL nAction
LOCAL aPairList
LOCAL aSortedList
/* 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_BUILD_CONTRIBS" )
/* Compatibility */
IF Empty( cFilter )
cFilter := GetEnv( "HB_CONTRIBLIBS" )
ENDIF
IF ! Empty( cFilter )
OutStd( "! HB_BUILD_CONTRIBS: " + cFilter + hb_eol() )
ENDIF
IF cFilter == "no"
ErrorLevel( 0 )
RETURN
ENDIF
/* Determine the mode of operation */
aParams := hb_AParams()
aGNUMakeParams := hb_ATokens( Lower( GetEnv( "HB_MAKECMDGOALS" ) ) )
DO CASE
CASE AScanL( aParams, "clean" ) > 0
IF AScanL( aGNUMakeParams, "clean" ) > 0 .AND. ;
AScanL( aGNUMakeParams, "install" ) > 0 .AND. ;
AScanL( aGNUMakeParams, "install" ) > AScanL( aGNUMakeParams, "clean" )
nAction := _ACT_CLEAN
ELSE
nAction := _ACT_CLEAN
ENDIF
CASE AScanL( aParams, "install" ) > 0
IF AScanL( aGNUMakeParams, "clean" ) > 0 .AND. ;
AScanL( aGNUMakeParams, "install" ) > 0 .AND. ;
AScanL( aGNUMakeParams, "install" ) > AScanL( 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
/* 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
/* Clearing envvars that may interact with hbmk2 */
/* Saving 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" )
/* Preprocessing */
cBinDir := GetEnv( "HB_HOST_BIN_DIR" ) + hb_ps()
OutStd( "! Calculating build order for packages..." + hb_eol() )
aPairList := {}
FOR EACH hProject IN s_hPackageList
call_hbmk2_hbinfo( cBinDir, s_cBase + hProject:__enumKey() + "/" + hProject[ "cFileName" ], hProject )
DeptLinesToDeptPairList( aPairList, hProject:__enumKey(), hProject[ "aDept" ] )
NEXT
aSortedList := TopoSort( aPairList )
/* Converting build options to hbmk2 options */
hb_setenv( "_HB_BUILD_INSTALL" )
cOptions := ""
IF nAction == _ACT_CLEAN
cOptions += " -clean"
ELSE
cOptions += " -inc"
IF nAction == _ACT_INC_REBUILD_INST
cOptions += " -rebuild"
ENDIF
IF nAction == _ACT_INC_INST .OR. ;
nAction == _ACT_INC_REBUILD_INST
hb_setenv( "_HB_BUILD_INSTALL", "yes" )
ENDIF
ENDIF
/* Start building */
OutStd( hb_StrFormat( "! Started package %1$s...", hActions[ nAction ] ) + hb_eol() )
FOR EACH cPackage IN aSortedList DESCEND
IF Empty( aFilter ) .OR. ;
iif( lFilterNegative,;
AScan( aFilter, {| tmp | tmp == cPackage } ) == 0,;
AScan( aFilter, {| tmp | tmp == cPackage } ) > 0 )
cProject := s_cBase + cPackage + "/" + s_hPackageList[ cPackage ][ "cFileName" ]
call_hbmk2( cBinDir, cProject, cOptions, "" )
/* Highly experimental */
IF s_hPackageList[ cPackage ][ "cType" ] == "hblib" .AND. ;
GetEnv( "HB_BUILD_CONTRIB_DLL" ) == "yes" .AND. ;
hb_FileExists( PathSepToSelf( FNameExtSet( cProject, ".hbc" ) ) )
call_hbmk2( cBinDir, cProject, cOptions, " -hbdyn -nohblib- -implib " + FNameExtSet( cProject, ".hbc" ) )
ENDIF
/* Compile documentation */
IF nAction == _ACT_INC_INST .OR. ;
nAction == _ACT_INC_REBUILD_INST
mk_hbd( PathSepToSelf( cPackage ) )
ENDIF
ELSE
/* OutStd( hb_StrFormat( "! package '%1$s' skipped due to custom filter", cPackage ) + hb_eol() ) */
ENDIF
NEXT
OutStd( hb_eol() )
OutStd( hb_StrFormat( "! Finished package %1$s...", hActions[ nAction ] ) + hb_eol() )
ErrorLevel( 0 )
RETURN
STATIC PROCEDURE clear_hbmk2_envvars()
/* Making sure that user settings do not interfere with the std build process. */
hb_setenv( "HBMK_OPTIONS" )
hb_setenv( "HARBOUR" )
hb_setenv( "HARBOURCMD" )
hb_setenv( "CLIPPER" )
hb_setenv( "CLIPPERCMD" )
RETURN
STATIC FUNCTION call_hbmk2_hbinfo( cBinDir, cProject, hProject )
LOCAL cStdOut
LOCAL cDir
LOCAL cName
LOCAL tmp
hProject[ "cType" ] := ""
hProject[ "aDept" ] := {}
clear_hbmk2_envvars()
IF hb_processRun( PathSepToSelf( cBinDir ) + "hbmk2 --hbinfo " + StrTran( cProject, "\", "/" ),, @cStdOut ) == 0
hProject[ "cType" ] := hbmk2_hbinfo_getitem( cStdOut, "targettype" )
FOR EACH tmp IN hb_ATokens( hbmk2_hbinfo_getitem( cStdOut, "hbctree", .T. ), Chr( 10 ) )
IF ! Empty( tmp )
hb_FNameSplit( LTrim( tmp ), @cDir, @cName )
AAdd( hProject[ "aDept" ], { "nDepth" => Len( tmp ) - Len( LTrim( tmp ) ),;
"cDir" => cDir,;
"cName" => cName } )
ENDIF
NEXT
RETURN .T.
ENDIF
RETURN .F.
STATIC FUNCTION hbmk2_hbinfo_getitem( cString, cItem, lAll )
LOCAL cRetVal := ""
LOCAL nPos := 1
LOCAL tmp
DO WHILE ( tmp := hb_At( cItem + "{{", cString, nPos ) ) > 0
nPos := tmp + Len( cItem + "{{" )
IF ( tmp := hb_At( "}}", cString, nPos ) ) > 0
tmp := StrTran( SubStr( cString, nPos, tmp - nPos ), Chr( 13 ) )
IF lAll != NIL .AND. lAll
cRetVal += tmp
ELSE
/* Find the last occurrence, which is the root project */
cRetVal := tmp
ENDIF
ENDIF
ENDDO
RETURN cRetVal
STATIC FUNCTION call_hbmk2( cBinDir, cProject, cOptionsPre, cOptionsPost )
LOCAL nErrorLevel
clear_hbmk2_envvars()
nErrorLevel := mk_hb_processRun( PathSepToSelf( cBinDir ) + "hbmk2" +;
" -quiet -lang=en -width=0" +;
" @" + StrTran( s_cHome + "hbpre", "\", "/" ) +;
cOptionsPre +;
" " + StrTran( cProject, "\", "/" ) +;
" @" + StrTran( s_cHome, "\", "/" ) + "hbpost" +;
cOptionsPost )
IF nErrorLevel != 0
OutStd( hb_StrFormat( "! '%1$s' returned status: %2$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 mk_hbd( cDir )
LOCAL cName
LOCAL tmp
LOCAL aErrMsg
LOCAL aEntry
IF ! Empty( GetEnv( "HB_DOC_INSTALL" ) )
cName := DirGetName( cDir )
IF Empty( cName )
cName := "harbour"
ENDIF
aErrMsg := {}
aEntry := __hbdoc_LoadDir( cDir, cName, aErrMsg )
FOR EACH tmp IN aErrMsg
OutErr( hb_StrFormat( "! %1$s", tmp ) + hb_eol() )
NEXT
IF ! Empty( aEntry )
cName := PathSepToSelf( GetEnv( "HB_DOC_INSTALL" ) ) + hb_ps() + cName + ".hbd"
IF __hbdoc_SaveHBD( cName, aEntry )
OutStd( "! Compiled documentation: " + cName + " <= " + cDir + hb_eol() )
RETURN .T.
ELSE
OutErr( hb_StrFormat( "! Error: Saving '%1$s'", cName ) + hb_eol() )
ENDIF
ENDIF
ENDIF
RETURN .F.
STATIC FUNCTION AScanL( aArray, cString )
RETURN AScan( aArray, {| tmp | Lower( tmp ) == cString } )
STATIC FUNCTION FNameDirGet( cFileName )
LOCAL cDir
hb_FNameSplit( cFileName, @cDir )
RETURN cDir
STATIC FUNCTION FNameExtSet( cFileName, cExt )
LOCAL cDir, cName
hb_FNameSplit( cFileName, @cDir, @cName )
RETURN hb_FNameMerge( cDir, cName, cExt )
STATIC FUNCTION FNameExtDef( cFileName, cDefExt )
LOCAL cDir, cName, cExt
hb_FNameSplit( cFileName, @cDir, @cName, @cExt )
IF Empty( cExt )
cExt := cDefExt
ENDIF
RETURN hb_FNameMerge( cDir, cName, cExt )
STATIC FUNCTION DirGetName( cDir )
LOCAL cName
cDir := DirDelPathSep( cDir )
hb_FNameSplit( cDir,, @cName )
IF Empty( cName ) .OR. cName == "." .OR. cName == ".."
RETURN ""
ENDIF
RETURN cName
STATIC FUNCTION DirAddPathSep( cDir )
IF ! Empty( cDir ) .AND. !( Right( cDir, 1 ) == hb_ps() )
cDir += hb_ps()
ENDIF
RETURN cDir
STATIC FUNCTION DirDelPathSep( cDir )
IF Empty( hb_osDriveSeparator() )
DO WHILE Len( cDir ) > 1 .AND. Right( cDir, 1 ) == hb_ps()
cDir := hb_StrShrink( cDir, 1 )
ENDDO
ELSE
DO WHILE Len( cDir ) > 1 .AND. Right( cDir, 1 ) == hb_ps() .AND. ;
!( Right( cDir, 2 ) == hb_osDriveSeparator() + hb_ps() )
cDir := hb_StrShrink( cDir, 1 )
ENDDO
ENDIF
RETURN cDir
STATIC FUNCTION PathSepToSelf( cFileName )
RETURN StrTran( cFileName, iif( hb_ps() == "\", "/", "\" ), hb_ps() )
STATIC FUNCTION hb_pwd()
RETURN DirAddPathSep( hb_CurDrive() + hb_osDriveSeparator() + hb_ps() + CurDir() )
/* Convert indented list of line to tree / list of parent-child pairs */
STATIC PROCEDURE DeptLinesToDeptPairList( aPairList, cParent, aFlatTree )
LOCAL hFlatTreeElement
LOCAL hNode, hNewNode, tmp
LOCAL nLevel, nDepth
AddDeptPair( aPairList, "", cParent )
hNode := { "child" => {}, "name" => cParent, "parent" => NIL }
nLevel := 0
FOR EACH hFlatTreeElement IN aFlatTree
/* Min() protects against jumping more than one level down in one step */
nDepth := Min( hFlatTreeElement[ "nDepth" ], nLevel + 1 )
hNewNode := { "child" => {}, "name" => hFlatTreeElement[ "cName" ], "cargo" => hFlatTreeElement }
IF nDepth > nLevel
hNode := ATail( hNode[ "child" ] )
ELSEIF nDepth < nLevel
FOR tmp := nDepth + 1 TO nLevel
hNode := hNode[ "parent" ]
NEXT
ENDIF
hNewNode[ "parent" ] := hNode
AAdd( hNode[ "child" ], hNewNode )
nLevel := nDepth
AddDeptPair( aPairList, hNewNode[ "parent" ][ "name" ], hNewNode[ "name" ] )
NEXT
RETURN
/* Add parent-child dependency to the list */
STATIC PROCEDURE AddDeptPair( aPairList, cParent, cChild )
IF AScan( aPairList, {| tmp | tmp[ 1 ] == cParent .AND. tmp[ 2 ] == cChild } ) == 0
AAdd( aPairList, { cParent, cChild } )
ENDIF
RETURN
/* Topological sort of the dependency graph */
STATIC FUNCTION TopoSort( aEdgeList )
LOCAL aList := {}
LOCAL hTopNodes := { => }
LOCAL n, m
LOCAL tmp
hb_HKeepOrder( hTopNodes, .T. )
FOR EACH n IN aEdgeList
IF AScan( aEdgeList, {| tmp | tmp[ 2 ] == n[ 1 ] } ) == 0
hTopNodes[ n[ 1 ] ] := NIL
ENDIF
NEXT
DO WHILE ! Empty( hTopNodes )
n := hb_HKeyAt( hTopNodes, 1 )
hb_HDelAt( hTopNodes, 1 )
IF ! Empty( n )
AAdd( aList, n )
ENDIF
FOR EACH tmp IN aEdgeList
IF tmp[ 1 ] == n
m := tmp[ 2 ]
tmp[ 1 ] := tmp[ 2 ] := NIL /* set to invalid value. TOOPT: Delete this member from list */
IF AScan( aEdgeList, {| tmp | tmp[ 2 ] == m } ) == 0
hTopNodes[ m ] := NIL
ENDIF
ENDIF
NEXT
ENDDO
FOR EACH tmp IN aEdgeList
IF !( tmp[ 1 ] == NIL .AND. tmp[ 2 ] == NIL )
OutStd( hb_StrFormat( "! Warning: Circular reference in dependency tree (%1$s - %2$s)", tmp[ 1 ], tmp[ 2 ] ) + hb_eol() )
ENDIF
NEXT
RETURN aList
PROCEDURE AddPkg( hPackageList, cFileName )
LOCAL cDir
LOCAL cName
LOCAL cExt
IF ! Empty( cFileName )
hb_FNameSplit( PathSepToSelf( AllTrim( cFileName ) ), @cDir, @cName, @cExt )
IF ! Empty( cName ) .AND. Empty( cDir )
cDir := cName
ENDIF
IF Empty( cName )
cName := DirGetName( cDir )
ENDIF
IF Empty( cExt )
cExt := ".hbp"
ENDIF
hPackageList[ StrTran( DirDelPathSep( cDir ), "\", "/" ) ] := { "cFileName" => hb_FNameMerge(, cName, cExt ) }
ENDIF
RETURN
PROCEDURE LoadPkgListFromFile( hPackageList, cFileName )
LOCAL cFile := StrTran( MemoRead( cFileName ), Chr( 13 ) )
LOCAL cItem
FOR EACH cItem IN hb_ATokens( cFile, Chr( 10 ) )
IF "#" $ cItem
cItem := Left( cItem, At( "#", cItem ) - 1 )
ENDIF
AddPkg( hPackageList, cItem )
NEXT
RETURN
PROCEDURE LoadPkgListFromString( hPackageList, cString )
LOCAL cItem
FOR EACH cItem IN hb_ATokens( cString,, .T. )
AddPkg( hPackageList, cItem )
NEXT
RETURN