Files
harbour-core/harbour/contrib/make.hbs
Viktor Szakats b12f96188a 2010-12-12 00:56 UTC+0200 Viktor Szakats (harbour.01 syenar.hu)
* contrib/hbqt/hbqt_hbmk2_plugin.hbs
    + Showing -v output (version string) of detected QT tools in -info mode.

  * contrib/make.hbs
    ! Typo.
2010-12-12 00:00:15 +00:00

889 lines
27 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 (long term, low priority):
1. normalize path to hbpre/hbpost to save on cmdline
(after normalizer function has been moved to core)
2. Move lib output dir and workdirs inside contrib dirs.
3. Make home project detection more robust in stdalone mode.
NOTE:
- 'install' is ignored in stdalone mode. It would be needed to
replicate the install dir defaulting logic found in global.mk
to implement it.
*/
#pragma warninglevel=3
#pragma -km+
#pragma -ko+
#include "directry.ch"
#define _ACT_INC_CLEAN 1
#define _ACT_INC 2
#define _ACT_INC_INST 3
#define _ACT_INC_REBUILD 4
#define _ACT_INC_REBUILD_INST 5
STATIC hActions := {;
_ACT_INC_CLEAN => "clean" ,;
_ACT_INC => "build" ,;
_ACT_INC_INST => "build and install" ,;
_ACT_INC_REBUILD => "rebuild" ,;
_ACT_INC_REBUILD_INST => "rebuild and install" }
STATIC s_cBase
STATIC s_cHome
STATIC s_cRoot
STATIC s_cBinDir
STATIC s_cReBase
PROCEDURE Main( ... )
LOCAL hProjectList
s_cBase := ""
s_cReBase := ""
IF Empty( GetEnv( "HB_HOST_BIN_DIR" ) )
s_cHome := StrTran( hb_DirBase(), hb_ps(), "/" )
s_cRoot := s_cHome + "../"
ELSE
s_cHome := ""
s_cRoot := "../"
ENDIF
#if defined( __HBSCRIPT__HBRUN )
s_cBinDir := hbrun_DirBase()
#else
s_cBinDir := hb_DirBase()
#endif
/* Load list of projects */
hProjectList := { => }
hb_HKeepOrder( hProjectList, .T. )
LoadProjectListFromFile( hProjectList, s_cHome + "hbplist" )
LoadProjectListFromString( hProjectList, GetEnv( "HB_BUILD_ADDONS" ) )
/* Build */
IF Empty( GetEnv( "HB_HOST_BIN_DIR" ) )
Standalone( hb_AParams(), hProjectList )
ELSE
GNUMake( hb_AParams(), hProjectList )
ENDIF
RETURN
/* Workflow translation for standalone operation:
GNU Make parameter nAction hbmk2 options
-- -------------- -------------- ---------------------- -------------------------
#1 clean clean _ACT_INC_CLEAN -inc -clean
#2 _ACT_INC -inc
#3 clean all clean all _ACT_INC_REBUILD -inc -rebuildall
#4 install install _ACT_INC_INST -inc -instpath=
#5 clean install clean install _ACT_INC_REBUILD_INST -inc -rebuildall -instpath=
*/
PROCEDURE Standalone( aParams, hProjectList )
LOCAL hProjectReqList
LOCAL cOptionsUser
LOCAL nAction
LOCAL tmp
LOCAL tmp1
LOCAL lCustom
/* Processing cmdline options */
DO CASE
CASE AScanL( aParams, "clean" ) > 0 .AND. ;
AScanL( aParams, "all" ) > 0 .AND. ;
AScanL( aParams, "all" ) > AScanL( aParams, "clean" )
nAction := _ACT_INC_REBUILD
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_INC_CLEAN
CASE AScanL( aParams, "install" ) > 0
nAction := _ACT_INC_INST
OTHERWISE
nAction := _ACT_INC
ENDCASE
/* Strip install action */
DO CASE
CASE nAction == _ACT_INC_REBUILD_INST ; nAction := _ACT_INC_REBUILD
CASE nAction == _ACT_INC_INST ; nAction := _ACT_INC
ENDCASE
/* Processing user options */
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
/* Assemble list of primary targets (registered projects in current directory) */
hProjectReqList := { => }
hb_HKeepOrder( hProjectReqList, .T. )
IF ! lCustom
/* Find out which projects are in current dir, these will be our primary targets */
FOR EACH tmp IN hProjectList
tmp1 := hb_ps() + FNameDirGet( PathSepToSelf( tmp:__enumKey() ) )
IF tmp1 == Right( hb_pwd(), Len( tmp1 ) ) /* Not ultimate solution */
hProjectReqList[ tmp:__enumKey() ] := tmp:__enumKey()
s_cReBase := SubStr( tmp1, 2 )
ENDIF
NEXT
IF Empty( hProjectReqList )
lCustom := .T.
ELSE
OutStd( hb_StrFormat( "! Package %1$s... %2$s project(s)", hActions[ nAction ], hb_ntos( Len( hProjectReqList ) ) ) + hb_eol() )
ENDIF
ENDIF
IF lCustom
mk_hb_processRun( s_cBinDir + "hbmk2" + cOptionsUser )
RETURN
ENDIF
/* Start building */
build_projects( nAction, hProjectList, hProjectReqList, cOptionsUser )
RETURN
/* Workflow translation from GNU Make to hbmk2:
GNU Make parameter HB_MAKECMDGOALS nAction hbmk2 options
-- -------------- ---------- ---------------- ---------------------- -------------------------
#1 clean clean clean _ACT_INC_CLEAN -inc -clean
#2 all _ACT_INC -inc
#3 install install install _ACT_INC_INST -inc -instpath=
#4 clean all clean clean all _ACT_INC_CLEAN -inc -clean
first clean all _ACT_INC_REBUILD -inc -rebuildall
#5 clean install clean clean install _ACT_INC_CLEAN -inc -clean
install clean install _ACT_INC_REBUILD_INST -inc -rebuildall -instpath=
#6 install clean install install clean _ACT_INC_INST -inc -instpath=
clean install clean _ACT_INC_CLEAN -inc -clean
*/
PROCEDURE GNUMake( aParams, hProjectList )
LOCAL cProject
LOCAL hProjectReqList
LOCAL cFilter
LOCAL aFilter
LOCAL lFilterNegative
LOCAL aGNUMakeParams
LOCAL nAction
LOCAL tmp
/* 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
/* Determine the mode of operation */
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_INC_CLEAN
ELSE
nAction := _ACT_INC_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. -rebuildall is
costless, so we do it to make sure to build cleanly.
[vszakats] */
nAction := _ACT_INC_REBUILD_INST
ELSE
nAction := _ACT_INC_INST
ENDIF
CASE AScanL( aParams, "first" ) > 0
IF AScanL( aGNUMakeParams, "clean" ) > 0 .AND. ;
AScanL( aGNUMakeParams, "all" ) > 0 .AND. ;
AScanL( aGNUMakeParams, "all" ) > AScanL( aGNUMakeParams, "clean" )
nAction := _ACT_INC_REBUILD
ELSE
nAction := _ACT_INC
ENDIF
OTHERWISE
nAction := _ACT_INC
ENDCASE
/* Assemble list of projects to be built */
cFilter := GetEnv( "HB_BUILD_CONTRIBS" )
IF ! Empty( cFilter )
OutStd( "! HB_BUILD_CONTRIBS: " + cFilter + hb_eol() )
ENDIF
IF cFilter == "no"
RETURN
ENDIF
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
hProjectReqList := { => }
hb_HKeepOrder( hProjectReqList, .T. )
FOR EACH tmp IN hProjectList
hProjectReqList[ tmp:__enumKey() ] := tmp:__enumKey()
NEXT
IF ! Empty( aFilter )
IF ! lFilterNegative
hProjectReqList := { => }
ENDIF
FOR EACH cProject IN aFilter
FOR EACH tmp IN hProjectList
IF hb_FileMatch( PathSepToSelf( cProject ), PathSepToSelf( tmp:__enumKey() ) ) .OR. ;
hb_FileMatch( PathSepToSelf( cProject ), DirDelPathSep( FNameDirGet( PathSepToSelf( tmp:__enumKey() ) ) ) )
IF lFilterNegative
IF tmp:__enumKey() $ hProjectReqList
hb_HDel( hProjectReqList, tmp:__enumKey() )
ENDIF
ELSE
hProjectReqList[ tmp:__enumKey() ] := tmp:__enumKey()
ENDIF
ENDIF
NEXT
NEXT
ENDIF
IF Empty( hProjectReqList )
RETURN
ENDIF
/* Clearing envvars that may interact with hbmk2 */
/* Saving original install dirs to our own variables */
hb_setenv( "_HB_INSTALL_BIN", GetEnv( "HB_INSTALL_BIN" ) )
hb_setenv( "_HB_INSTALL_LIB", GetEnv( "HB_INSTALL_LIB" ) )
hb_setenv( "_HB_INSTALL_DYN", GetEnv( "HB_INSTALL_DYN" ) )
hb_setenv( "_HB_INSTALL_INC", GetEnv( "HB_INSTALL_INC" ) )
hb_setenv( "_HB_INSTALL_MAN", GetEnv( "HB_INSTALL_MAN" ) )
hb_setenv( "_HB_INSTALL_ETC", GetEnv( "HB_INSTALL_ETC" ) )
/* Override hbmk2 autodetection. WARNING: Must be in sync with global.mk logic */
hb_setenv( "HB_INSTALL_PREFIX", s_cRoot )
hb_setenv( "HB_INSTALL_BIN", s_cRoot + "bin/" + GetEnv( "HB_PLATFORM" ) + "/" + GetEnv( "HB_COMPILER" ) + GetEnv( "HB_BUILD_NAME" ) )
hb_setenv( "HB_INSTALL_LIB", s_cRoot + "lib/" + GetEnv( "HB_PLATFORM" ) + "/" + GetEnv( "HB_COMPILER" ) + GetEnv( "HB_BUILD_NAME" ) )
hb_setenv( "HB_INSTALL_DYN" )
hb_setenv( "HB_INSTALL_INC", s_cRoot + "include" )
/* Start building */
OutStd( hb_StrFormat( "! Started package %1$s...", hActions[ nAction ] ) + hb_eol() )
build_projects( nAction, hProjectList, hProjectReqList, "" )
OutStd( hb_StrFormat( "! Finished package %1$s...", hActions[ nAction ] ) + hb_eol() )
RETURN
STATIC PROCEDURE build_projects( nAction, hProjectList, hProjectReqList, cOptionsUser )
LOCAL aPairList
LOCAL aSortedList
LOCAL cOptions
LOCAL lInstall
LOCAL cProject
LOCAL cProjectPath
LOCAL lPrimary
LOCAL lContainer
LOCAL cDynSuffix
LOCAL nErrorLevel
/* Preprocessing */
IF Len( hProjectReqList ) > 1
OutStd( hb_StrFormat( "! Calculating build order for %1$s projects...", hb_ntos( Len( hProjectReqList ) ) ) + hb_eol() )
ENDIF
aPairList := {}
FOR EACH cProject IN hProjectReqList
call_hbmk2_hbinfo( s_cBase + s_cHome + cProject, hProjectList[ cProject ] )
DeptLinesToDeptPairList( aPairList, cProject, hProjectList[ cProject ][ "aDept" ] )
NEXT
aSortedList := TopoSort( aPairList )
/* Add referenced project not present on our list and featuring an .hbp file */
FOR EACH cProject IN aSortedList
IF !( cProject $ hProjectList )
IF hb_FileExists( s_cBase + s_cHome + cProject )
AddProject( hProjectList, cProject )
call_hbmk2_hbinfo( s_cBase + s_cHome + cProject, hProjectList[ cProject ] )
hProjectList[ cProject ][ "lFromContainer" ] := NIL
ENDIF
ENDIF
NEXT
/* Load project information for dependencies too
(we need "cType" to decide about dynamic build) */
IF GetEnv( "HB_BUILD_CONTRIB_DYN" ) == "yes"
FOR EACH cProject IN aSortedList
IF !( cProject $ hProjectReqList ) .AND. ;
cProject $ hProjectList .AND. ;
!( "lChecked" $ hProjectList[ cProject ] )
call_hbmk2_hbinfo( s_cBase + s_cHome + cProject, hProjectList[ cProject ] )
ENDIF
NEXT
ENDIF
/* Convert action to hbmk2 options */
cOptions := " -inc"
IF nAction == _ACT_INC_CLEAN
cOptions += " -clean"
ELSEIF nAction == _ACT_INC_REBUILD .OR. ;
nAction == _ACT_INC_REBUILD_INST
cOptions += " -rebuildall"
ENDIF
lInstall := nAction == _ACT_INC_INST .OR. ;
nAction == _ACT_INC_REBUILD_INST
hb_setenv( "_HB_BUILD_INSTALL", iif( lInstall, "yes", NIL ) )
/* Build the dependencies and primary targets in sorted order */
FOR EACH cProject IN aSortedList DESCEND
IF cProject $ hProjectList
cProjectPath := s_cBase + s_cHome + cProject
lPrimary := cProject $ hProjectReqList
lContainer := "lFromContainer" $ hProjectList[ cProject ]
IF ( nErrorLevel := call_hbmk2( cProjectPath, iif( lPrimary .OR. lContainer, iif( lContainer, cOptions, cOptions + cOptionsUser ), " -inc" ), NIL ) ) == 0
/* Build dynamic lib */
IF GetEnv( "HB_BUILD_CONTRIB_DYN" ) == "yes" .AND. hProjectList[ cProject ][ "cType" ] == "hblib"
/* Is this a platform where import libs are used? */
IF "|" + hProjectList[ cProject ][ "cPlatform" ] + "|" $ "|win|dos|os2|"
IF Empty( hProjectList[ cProject ][ "cDynSuffix" ] )
cDynSuffix := "_dll"
ELSE
cDynSuffix := hProjectList[ cProject ][ "cDynSuffix" ]
ENDIF
ELSE
cDynSuffix := hb_libExt()
ENDIF
call_hbmk2( cProjectPath, iif( lPrimary .OR. lContainer, iif( lContainer, cOptions, cOptions + cOptionsUser ), " -inc" ), cDynSuffix )
ENDIF
IF lPrimary .OR. lContainer
/* Compile documentation */
IF lInstall
mk_hbd( FNameDirGet( PathSepToSelf( cProjectPath ) ) )
ENDIF
/* Create EXTERN list */
IF hProjectList[ cProject ][ "cType" ] $ "hblib|hbdyn"
mk_extern_lib( PathSepToSelf( hProjectList[ cProject ][ "cOutputName" ] ),;
FNameExtSet( PathSepToSelf( cProjectPath ), ".hbx" ) )
ENDIF
ENDIF
ELSE
/* Ignore certain non-fatal hbmk2 return values */
IF nErrorLevel != 10 .AND. ;
nErrorLevel != 20 .AND. ;
nErrorLevel != 50
ErrorLevel( nErrorLevel )
EXIT
ENDIF
ENDIF
ENDIF
NEXT
RETURN
STATIC FUNCTION call_hbmk2_hbinfo( cProjectPath, hProject )
LOCAL cStdOut
LOCAL cDir
LOCAL cName
LOCAL tmp
LOCAL nErrorLevel
hProject[ "cType" ] := ""
hProject[ "aDept" ] := {}
hProject[ "lChecked" ] := NIL
IF ( nErrorLevel := call_hbmk2( cProjectPath, " --hbinfo", NIL,, @cStdOut ) ) == 0
hProject[ "cType" ] := hbmk2_hbinfo_getitem( cStdOut, "targettype" )
hProject[ "cOutputName" ] := hbmk2_hbinfo_getitem( cStdOut, "outputname" )
hProject[ "cDynSuffix" ] := hbmk2_hbinfo_getitem( cStdOut, "dynsuffix" )
hProject[ "cPlatform" ] := hbmk2_hbinfo_getitem( cStdOut, "platform" )
FOR EACH tmp IN hb_ATokens( hbmk2_hbinfo_getitem( cStdOut, "hbctree", .T. ), Chr( 10 ) )
IF ! Empty( tmp )
hb_FNameSplit( LTrim( tmp ), @cDir, @cName )
#ifdef __PLATFORM__DOS
/* Ignore long filenames on MS-DOS hosts */
IF Len( cName ) > 8
LOOP
ENDIF
#endif
AAdd( hProject[ "aDept" ], { "nDepth" => Len( tmp ) - Len( LTrim( tmp ) ),;
"cFileName_HBP" => StrTran( PathNormalize( PathMakeAbsolute( FNameExtSet( PathSepToSelf( LTrim( tmp ) ), ".hbp" ), s_cRebase ) ), "\", "/" ) } )
ENDIF
NEXT
ENDIF
RETURN nErrorLevel
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( cProjectPath, cOptionsPre, cDynSuffix, cStdErr, cStdOut )
LOCAL nErrorLevel
LOCAL cOptionsLibDyn := ""
LOCAL cCommand
/* 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" )
IF cDynSuffix != NIL
hb_setenv( "__HB_DYN__", cDynSuffix ) /* Request dll version of Harbour contrib dependencies (the implibs) to be linked (experimental) */
hb_setenv( "_HB_BUILD_LIBDYN", "yes" )
IF hb_FileExists( FNameExtSet( cProjectPath, ".hbc" ) )
cOptionsLibDyn += " " + FNameExtSet( cProjectPath, ".hbc" )
ENDIF
ELSE
hb_setenv( "__HB_DYN__" )
hb_setenv( "_HB_BUILD_LIBDYN" )
ENDIF
cCommand := s_cBinDir + "hbmk2" +;
" -quiet -width=0" +;
" @" + StrTran( s_cHome + "hbpre", "\", "/" ) +;
cOptionsPre +;
" " + StrTran( cProjectPath, "\", "/" ) +;
" @" + StrTran( s_cHome, "\", "/" ) + "hbpost" +;
cOptionsLibDyn
IF PCount() >= 4
nErrorLevel := hb_processRun( cCommand,, @cStdOut, @cStdErr )
ELSE
nErrorLevel := mk_hb_processRun( cCommand )
ENDIF
IF nErrorLevel != 0
OutStd( hb_StrFormat( "! '%1$s' returned status: %2$s", cProjectPath, hb_ntos( nErrorLevel ) ) + hb_eol() )
ENDIF
RETURN nErrorLevel
STATIC FUNCTION mk_hb_processRun( cCommand, ... )
OutStd( cCommand + hb_eol() )
RETURN hb_processRun( cCommand, ... )
STATIC FUNCTION mk_extern_lib( cInputName, cOutputName )
LOCAL aExtern
IF GetEnv( "HB_REBUILD_EXTERN" ) == "yes"
IF ( aExtern := __hb_extern_get_list( cInputName ) ) != NIL
OutStd( hb_StrFormat( "! Generating extern header: %1$s", cOutputName ) + hb_eol() )
RETURN __hb_extern_gen( aExtern, cOutputName )
ENDIF
ENDIF
RETURN .F.
STATIC FUNCTION mk_hbd( cDir )
LOCAL cName
LOCAL cDocDir
LOCAL tmp
LOCAL aErrMsg
LOCAL aEntry
IF ! Empty( cDocDir := GetEnv( "HB_INSTALL_DOC" ) ) .AND. ! cDocDir == "no"
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( cDocDir ) + 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() )
STATIC FUNCTION PathMakeAbsolute( cPathR, cPathA )
LOCAL cDirA
LOCAL cDirR, cDriveR, cNameR, cExtR
IF Empty( cPathA )
RETURN cPathR
ENDIF
hb_FNameSplit( cPathR, @cDirR, @cNameR, @cExtR, @cDriveR )
IF ! Empty( cDriveR ) .OR. ( ! Empty( cDirR ) .AND. Left( cDirR, 1 ) $ hb_osPathDelimiters() )
RETURN cPathR
ENDIF
hb_FNameSplit( cPathA, @cDirA )
IF Empty( cDirA )
RETURN cPathR
ENDIF
RETURN hb_FNameMerge( cDirA + cDirR, cNameR, cExtR )
#define _ISDRIVESPEC( cDir ) ( ! Empty( hb_osDriveSeparator() ) .AND. Right( cDir, Len( hb_osDriveSeparator() ) ) == hb_osDriveSeparator() )
/* NOTE: Can hurt if there are symlinks on the way. */
STATIC FUNCTION PathNormalize( cPath )
LOCAL aDir
LOCAL cDir
IF ! Empty( cPath )
aDir := hb_ATokens( cPath, hb_ps() )
FOR EACH cDir IN aDir DESCEND
IF cDir == "."
hb_ADel( aDir, cDir:__enumIndex(), .T. )
ELSEIF !( cDir == ".." ) .AND. ;
! Empty( cDir ) .AND. ;
! _ISDRIVESPEC( cDir )
IF cDir:__enumIndex() < Len( cDir:__enumBase() ) .AND. ;
aDir[ cDir:__enumIndex() + 1 ] == ".."
hb_ADel( aDir, cDir:__enumIndex() + 1, .T. )
hb_ADel( aDir, cDir:__enumIndex(), .T. )
ENDIF
ENDIF
NEXT
cPath := ""
FOR EACH cDir IN aDir
cPath += cDir
IF cDir:__enumIndex() < Len( cDir:__enumBase() )
cPath += hb_ps()
ENDIF
NEXT
IF Empty( cPath )
cPath := "." + hb_ps()
ENDIF
ENDIF
RETURN cPath
/* 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[ "cFileName_HBP" ], "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 AddProject( hProjectList, cFileName )
LOCAL cDir
LOCAL cName
LOCAL cExt
IF ! Empty( cFileName )
cFileName := PathSepToSelf( AllTrim( cFileName ) )
hb_FNameSplit( 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
cFileName := hb_FNameMerge( cDir, cName, cExt )
hProjectList[ StrTran( cFileName, "\", "/" ) ] := { => }
ENDIF
RETURN
PROCEDURE LoadProjectListFromFile( hProjectList, cFileName )
LOCAL cItem
FOR EACH cItem IN hb_ATokens( StrTran( MemoRead( cFileName ), Chr( 13 ) ), Chr( 10 ) )
IF "#" $ cItem
cItem := Left( cItem, At( "#", cItem ) - 1 )
ENDIF
AddProject( hProjectList, cItem )
NEXT
RETURN
PROCEDURE LoadProjectListFromString( hProjectList, cString )
LOCAL cItem
FOR EACH cItem IN hb_ATokens( cString,, .T. )
AddProject( hProjectList, cItem )
NEXT
RETURN
#include "../config/hbextern.hbs"