* contrib/hbqt/hbqt_hbmk2_plugin.hbs
+ Showing -v output (version string) of detected QT tools in -info mode.
* contrib/make.hbs
! Typo.
889 lines
27 KiB
Handlebars
Executable File
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"
|