* utils/hbmk2/hbmk2.*.po
* utils/hbmk2/hbmk2.prg
+ added '-hb30' option and '{hb30}' filter macro to allow
reverting back to Harbour 3.0.0
+ added provisions for merging 'hblang' and 'hbcpage'
core libs into new 'hbnat' lib
+ SETCURSOR() and SETCOLOR() references will now also
trigger full screen CUI mode in hbrun scripts
+ documented the fact that GTCGI is default GT for scripts
and that it's switched to another one (and which) when
CUI mode script is detected
+ marked all information in help with '[*]' which shows
host platform dependent, dynamic data (f.e. systems paths)
+ hbmk.hbc will now be searched for under user's home
directory on non-*nix, too
+ added '-find' special hbmk2 option for finding Harbour functions.
It will lookup any strings passed as command line arguments
and display in which library they can be found. It will
also do the lookup in packages not currently installed.
Replaces similar 'hbrun bin/find' functionality, but now
uses existing code inside hbmk2. The new implementation
also supports wildcards.
Example:
hbmk2 -find wapi_*string ntos wild
! documented that hbstart.hb is first searched for in
current working directory
! ${hb_ver} and ${hb_verstr} macros now change their value
in compatibility modes (-hb10, -hb20, -hb30, -xhb)
+ -longhelpmd output will now not contain installation
specific (and potentially sensitive) data: home directory,
hbmk2 directory, only generic replacement terms
+ version= directive help text got a mention of its default
value (it was so far only mentioned next to HBMK_HAS_<hbcname>
envvar)
+ documented exit codes in help text
* minor tweaks to some help lines
* cleanup for Markdown formatting internals
* plugin callback variable "nErrorLevel" renamed to "nExitCode"
[INCOMPATIBLE]
* exit code changed to value 6 from 1 when hbrun script has
a compile error [INCOMPATIBLE]
+ use core hb_DirSepToOS() where possible
- deleted hbmk2 plugin API function hbmk_PathSepToSelf()
Use core hb_DirSepToOS() instead. [INCOMPATIBLE]
+ added '-exitstr' hbmk2 option which will display the exit result
in textual format
* contrib/hbpre.hbm
* contrib/make.hb
% use -exitstr hbmk2 option instead of rolling a local copy
of possible result strings
* src/pp/ppcore.c
+ generate '\a' '\f' '\v' escape chars
- bin/find.hb
* config/postinst.hb
* package/harbour.spec
- deleted find.hb for 'hbrun bin/find' functionality.
Replaced by 'hbmk2 -find' option.
* src/rtl/Makefile
- src/rtl/strxchg.c
+ src/rtl/strrepl.c
* src/rtl/tget.prg
* utils/hbmk2/hbmk2.prg
* include/harbour.hbx
* ChangeLog.txt
* renamed HB_STRXCHG() to HB_STRREPLACE(), according to:
https://groups.google.com/d/topic/harbour-devel/vSzlAkv6h9Y/discussion
* updated function skeleton in ChangeLog and C source
(also with new hash parameter)
* contrib/hbsqlit3/hbsqlit3.hbp
* contrib/hbsqlit3/hbsqlit3.hbx
+ contrib/hbsqlit3/errstr.prg
+ hb_sqlite3_errstr_short( <nError> ) -> <cError>
* contrib/hbsqlit3/tests/authoriz.prg
* contrib/hbsqlit3/tests/backup.prg
* contrib/hbsqlit3/tests/hooks.prg
% use hb_sqlite3_errstr_short() instead of implementing
it locally in each example
% use sqlite3_errstr() API to get long error strings instead
of reimplementing it locally
% minor opt
* contrib/hbsqlit3/hbsqlit3.hbp
- contrib/hbsqlit3/hdbcsqlt.prg
+ contrib/hbsqlit3/hdbc.prg
* renamed
* contrib/hbhpdf/errstr.prg
* contrib/hbmzip/mziperr.prg
! indenting
* contrib/hbtip/tests/tiptest.prg
* leave color
766 lines
23 KiB
Plaintext
Executable File
766 lines
23 KiB
Plaintext
Executable File
#!/usr/bin/hbrun --hb:gtcgi
|
|
/*
|
|
* $Id$
|
|
*/
|
|
|
|
/*
|
|
* Harbour Project source code:
|
|
* Package build orchestrator script
|
|
*
|
|
* Copyright 2010 Viktor Szakats (harbour syenar.net)
|
|
* 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/).
|
|
*
|
|
*/
|
|
|
|
#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 sc_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
|
|
LOCAL aParams
|
|
|
|
hb_cdpSelect( "UTF8EX" )
|
|
|
|
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__HBSHELL )
|
|
s_cBinDir := hbshell_DirBase()
|
|
#else
|
|
s_cBinDir := hb_DirBase()
|
|
#endif
|
|
/* For *nixes */
|
|
s_cBinDir := hb_PathNormalize( s_cBinDir )
|
|
|
|
/* Load list of projects */
|
|
|
|
hProjectList := { => }
|
|
hb_HKeepOrder( hProjectList, .T. )
|
|
|
|
LoadProjectListFromFile( hProjectList, s_cHome + "hbplist" )
|
|
LoadProjectListFromString( hProjectList, GetEnv( "HB_BUILD_ADDONS" ) )
|
|
|
|
aParams := hb_AParams()
|
|
|
|
IF AScanL( aParams, "verbose" ) > 0
|
|
hb_SetEnv( "HB_BUILD_VERBOSE", "yes" )
|
|
ENDIF
|
|
|
|
/* Build */
|
|
IF Empty( GetEnv( "HB_HOST_BIN_DIR" ) )
|
|
Standalone( aParams, hProjectList )
|
|
ELSE
|
|
GNUMake( 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, "rebuild" ) > 0
|
|
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
|
|
|
|
/* 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" ) .AND. ;
|
|
!( Lower( tmp ) == "rebuild" ) .AND. ;
|
|
!( Lower( tmp ) == "verbose" )
|
|
|
|
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() + hb_FNameDir( hb_DirSepToOS( tmp:__enumKey() ) )
|
|
IF tmp1 == Right( hb_cwd(), 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$d project(s)", sc_hActions[ nAction ], 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, .T. )
|
|
|
|
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
|
|
CASE AScanL( aParams, "rebuild" ) > 0
|
|
nAction := _ACT_INC_REBUILD
|
|
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( hb_DirSepToOS( cProject ), hb_DirSepToOS( tmp:__enumKey() ) ) .OR. ;
|
|
hb_FileMatch( hb_DirSepToOS( cProject ), hb_DirSepDel( hb_FNameDir( hb_DirSepToOS( 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" ) )
|
|
hb_SetEnv( "_HB_INSTALL_CONTRIB", GetEnv( "HB_INSTALL_CONTRIB" ) )
|
|
|
|
/* 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...", sc_hActions[ nAction ] ) + hb_eol() )
|
|
|
|
build_projects( nAction, hProjectList, hProjectReqList, "", .F. )
|
|
|
|
OutStd( hb_StrFormat( "! Finished package %1$s...", sc_hActions[ nAction ] ) + hb_eol() )
|
|
|
|
RETURN
|
|
|
|
STATIC PROCEDURE build_projects( nAction, hProjectList, hProjectReqList, cOptionsUser, lStdAlone )
|
|
|
|
LOCAL aPairList
|
|
LOCAL aSortedList
|
|
|
|
LOCAL cOptions
|
|
LOCAL lInstall
|
|
LOCAL cMakeFlags
|
|
|
|
LOCAL cProject
|
|
LOCAL cProjectPath
|
|
LOCAL lPrimary
|
|
LOCAL lContainer
|
|
|
|
LOCAL cDynSuffix
|
|
|
|
LOCAL nErrorLevel
|
|
|
|
/* Signal that we're doing a Harbour build */
|
|
hb_SetEnv( "_HB_BUILD_", "yes" )
|
|
|
|
/* Preprocessing */
|
|
|
|
IF Len( hProjectReqList ) > 1
|
|
OutStd( hb_StrFormat( "! Calculating build order for %1$d projects...", 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 in 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
|
|
|
|
cMakeFlags := GetEnv( "MAKEFLAGS" )
|
|
IF " -j " $ " " + cMakeFlags + " "
|
|
/* GNU Make uses job server to limit number of concurrent operations
|
|
* We cannot read it from MAKEFLAGS so I set it to arbitrary value: 8
|
|
*/
|
|
cOptions += " -jobs=8"
|
|
ENDIF
|
|
|
|
lInstall := nAction == _ACT_INC_INST .OR. ;
|
|
nAction == _ACT_INC_REBUILD_INST
|
|
|
|
hb_SetEnv( iif( lStdAlone, "_HB_BUILD_INSTALL_STDALONE", "_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" ) + ;
|
|
iif( ( lPrimary .OR. lContainer ) .AND. ;
|
|
hProjectList[ cProject ][ "cType" ] $ "hblib|hbdyn" .AND. ;
|
|
GetEnv( "HB_REBUILD_EXTERN" ) == "yes", " -hbx=" + hb_FNameExtSet( cProjectPath, ".hbx" ), "" ), 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( hb_FNameDir( hb_DirSepToOS( cProjectPath ) ) )
|
|
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 hInfo
|
|
|
|
LOCAL nErrorLevel
|
|
|
|
hProject[ "cType" ] := ""
|
|
hProject[ "aDept" ] := {}
|
|
hProject[ "lChecked" ] := NIL
|
|
|
|
IF ( nErrorLevel := call_hbmk2( cProjectPath, " --hbinfo", NIL,, @cStdOut ) ) == 0
|
|
|
|
hb_jsonDecode( cStdOut, @hInfo )
|
|
|
|
hProject[ "cType" ] := hbmk2_hbinfo_getitem( hInfo, "targettype" )
|
|
hProject[ "cOutputName" ] := hbmk2_hbinfo_getitem( hInfo, "outputname" )
|
|
hProject[ "cDynSuffix" ] := hbmk2_hbinfo_getitem( hInfo, "dynsuffix" )
|
|
hProject[ "cPlatform" ] := hbmk2_hbinfo_getitem( hInfo, "platform" )
|
|
|
|
FOR EACH tmp IN hb_ATokens( hbmk2_hbinfo_getitem( hInfo, "hbctree" ), 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( hb_PathNormalize( hb_PathJoin( s_cRebase, hb_FNameExtSet( hb_DirSepToOS( LTrim( tmp ) ), ".hbp" ) ) ), "\", "/" ) } )
|
|
ENDIF
|
|
NEXT
|
|
ENDIF
|
|
|
|
RETURN nErrorLevel
|
|
|
|
STATIC FUNCTION hbmk2_hbinfo_getitem( hInfo, cItem )
|
|
RETURN iif( HB_ISHASH( hInfo ), hb_HGetDef( hInfo, cItem, "" ), "" )
|
|
|
|
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_DYNSUFF", cDynSuffix ) /* Request dll version of Harbour contrib dependencies (the implibs) to be linked (experimental) */
|
|
|
|
cOptionsPre += " -hbdyn"
|
|
|
|
IF hb_FileExists( hb_FNameExtSet( cProjectPath, ".hbc" ) )
|
|
cOptionsLibDyn += " " + hb_FNameExtSet( cProjectPath, ".hbc" )
|
|
ENDIF
|
|
ELSE
|
|
hb_SetEnv( "_HB_DYNSUFF" )
|
|
ENDIF
|
|
|
|
hb_SetEnv( "_HB_CONTRIB_SUBDIR", hb_FNameDir( hb_DirSepToOS( cProjectPath ) ) )
|
|
|
|
cCommand := s_cBinDir + "hbmk2" + ;
|
|
" -quiet -width=0 -autohbm-" + ;
|
|
" @" + 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
|
|
|
|
RETURN nErrorLevel
|
|
|
|
STATIC FUNCTION mk_hb_processRun( cCommand, ... )
|
|
|
|
OutStd( cCommand + hb_eol() )
|
|
|
|
RETURN hb_processRun( cCommand, ... )
|
|
|
|
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 := hb_DirSepToOS( 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 DirGetName( cDir )
|
|
|
|
LOCAL cName
|
|
|
|
cDir := hb_DirSepDel( cDir )
|
|
|
|
hb_FNameSplit( cDir,, @cName )
|
|
|
|
IF Empty( cName ) .OR. cName == "." .OR. cName == ".."
|
|
RETURN ""
|
|
ENDIF
|
|
|
|
RETURN cName
|
|
|
|
/* 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 := hb_DirSepToOS( 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
|