Algumas coisas pode ser por costume.
O hbmk2 detecta o compilador que estiver no PATH, não lembro se na pasta COMP ele detecta automático.
Eventualmente, se tivermos mais de um e quisermos um específico, dá pra indicar também HB_COMPILER=mingw ou mingw64
Na prática, a pasta \tools\util tem mais coisas: UPX, editor de textos, c.bat, e outros.
Aliás.... renomeei o UPX.EXE e coloquei outro no lugar, pra alterar os parâmetros.
Outro detalhe: ao mudar de projeto limpo a pasta temp.
Supondo dois arquivos de teste, contendo test.prg, nomes iguais mas conteúdo diferente.
Uso -workdir=c:\temp
O bat é só aquilo mesmo.
O build.exe vai alterar o conteúdo após a chamada.
Código: Selecionar todos
/*
BUILD - PRE-COMPILACAO
José Quintas
*/
ANNOUNCE HB_GTSYS
REQUEST errorsys
#include "directry.ch"
STATIC stErro := .F.
FUNCTION Main( ... )
LOCAL aParamList, aSignList := {}, cDirMask, aCmdList := {}
SetMode( 33, 100 )
SetColor( "W/B" )
FOR EACH cDirMask IN { "ze_resource.*", "*.reso", "_hbmkaut*.*" }
AEval( Directory( "c:\temp\" + cDirMask ), { | e | fErase( "c:\temp\" + e[ F_NAME ] ) } )
NEXT
aParamList := hb_aParams()
CheckGetEnv( @aCmdList )
CheckPath( @aSignList, @aCmdList )
checkHbmkHbc()
CheckBuild()
WriteBat( aParamList, aSignList, aCmdList )
IF stErro
Inkey(3)
ENDIF
RETURN Nil
STATIC FUNCTION WriteBat( aParamList, aSignList, aCmdList )
LOCAL oElement, cPath, cCmdDefault := "", cCmd := "", aHbpList := {}, aList, cItem
LOCAL cBatFile := "d:\tools\util\c.bat", cFile, aFile, cHbp, lMultiHbp := .F.
cCmd := hb_ProgName() + " %*" + hb_Eol()
FOR EACH cItem IN aCmdList
cCmd += cItem + hb_Eol()
NEXT
cPath := Upper( hb_cwd() )
IF ! Empty( aSignList )
FOR EACH cFile IN aSignList
cCmd += "call assina " + cFile + hb_Eol()
NEXT
ENDIF
IF Len( aParamList ) != 0 .AND. aParamList[ 1 ] == "/cmd"
hb_ADel( aParamList, 1, .T. )
ELSE
IF hb_AScan( aParamList, { | e | e $ "|-w0|-w1|-w2|-w3|" } ) == 0
AAdd( aParamList, "-w3" )
ENDIF
IF hb_AScan( aParamList, { | e | e $ "|-es0|-es1|-es2|" } ) == 0
AAdd( aParamList, "-es2" )
ENDIF
IF hb_AScan( aParamList, { | e | e == "-m" } ) == 0
AAdd( aParamList, "-m" )
ENDIF
IF hb_ASCan( aParamList, { | e | e == "-n" } ) == 0
AAdd( aParamList, "-n" )
ENDIF
//IF ASCan( aParamList, "-gc3" ) == 0
// AAdd( aParamList, "-gc3" )
//ENDIF
IF Len( Directory( "*.hbp" ) ) != 0
aList := Directory( "*.hbp" )
FOR EACH aFile IN aList
IF Lower( aFile[ F_NAME ] ) != "hwguidyn.hbp" // error IF create it
AAdd( aHbpList, aFile[ F_NAME ] )
ENDIF
NEXT
ELSEIF Len( Directory( "*.prg" ) ) != 0
AAdd( aParamList, "*.prg" )
AAdd( aParamList, "*.rc" )
ELSE
ShowNormal( hb_cwd() )
ENDIF
ENDIF
AAdd( aParamList, "-strip" )
AAdd( aParamList, "-compr" )
AAdd( aParamList, "-workdir=c:\temp" )
AAdd( aParamList, "-q" )
AAdd( aParamList, "-I" + hb_FNameDir( hb_ProgName() ) )
IF "SAMPLE" $ Upper( hb_cwd() )
IF "HMGE" $ Upper( hb_cwd() )
AAdd( aParamList, "hmge.hbc" )
ENDIF
IF "HWGUI" $ Upper( hb_cwd() )
AAdd( aParamList, "hwgui.hbc" )
ENDIF
IF "OOHG" $ Upper( hb_cwd() )
AAdd( aParamList, "oohg.hbc" )
ENDIF
IF "FIVEWIN" $ Upper( hb_cwd() )
AAdd( aParamList, "fivewin.hbc" )
ENDIF
ENDIF
FOR EACH oElement IN aParamList
cCmdDefault += oElement + " "
NEXT
IF Len( aHbpList ) == 0
cCmd += "HBMK2 " + cCmdDefault + hb_Eol()
ELSE
IF Len( aHbpList ) > 1
lMultiHbp := .T.
ENDIF
FOR EACH cHbp IN aHbpList
IF lMultiHbp
//cCmd += "ECHO S | DEL C:\TEMP\*.*" + hb_Eol()
ENDIF
IF "-hbcontainer" $ MemoRead( cHbp ) .AND. ! "-rebuildall" $ cCmdDefault
cCmdDefault += " -rebuildall"
ENDIF
cCmd += "HBMK2 " + cHbp + " " + cCmdDefault + hb_Eol()
NEXT
ENDIF
DO CASE
CASE "\SJPA\" $ cPath; cCmd += "if exist ..\sjpa.exe call assina.bat ..\sjpa.exe" + hb_Eol()
CASE "\HAROLDO\" $ cPath; cCmd += "if exist hl.exe call assina.bat hl.exe" + hb_Eol()
CASE "\INTEGRA\" $ cPath; cCmd += "if exist jpa.exe call assina.bat jpa.exe" + hb_Eol()
CASE "\BUILD\" $ cPath; cCmd += "if exist build.exe call assina.bat build.exe" + hb_Eol()
CASE "\UTIL\UPX\" $ cPath
cCmd += "if exist pupx.exe call assina.bat pupx.exe" + hb_Eol()
cCmd += "if exist pupx.exe copy pupx.exe \tools\util\upx.exe" + hb_Eol()
ENDCASE
IF ! hb_DirExists( "c:\temp" )
hb_DirCreate( "c:\temp" )
ShowAlert( "Created c:\temp" )
ENDIF
hb_MemoWrit( cBatFile, cCmd )
RETURN Nil
STATIC FUNCTION CheckHbmkHbc()
LOCAL lUpdate := .F., cParam, cTxt := "", cFileHbmk, cPath
LOCAL cLib1 := "", cLib2, cPathLib := "d:\harbour\lib\win\mingw\"
LOCAL aParamList := { ;
"mt=yes", ;
"gui=yes", ;
"strip=yes", ;
"fullstatic=yes", ;
; // "{hbexe}compr=yes", ;
"PRGFLAGS=-m -n -w3 -es2 -ge1 -DMT_EXPERIMENTAL -DHB_NO_GTGUI=YES", ; // -DHB_NO_HWGUIDEBUG=YES", ;
"incpaths=d:/fontes/util/build", ;
"libpaths=d:/fontes/integra/libjpa", ;
"libpaths=d:/fontes/integra/boletoclass", ;
"libpaths=d:/fontes/integra/sefazclass", ;
"libpaths=d:/github/rmchartclass", ;
"libpaths=d:/github/wvgtest", ;
"libpaths=d:/github/hwgui", ;
"libpaths=d:/github/oohg", ;
"libpaths=d:/github/hmge", ;
"libpaths=d:/github/hmg3", ;
"libpaths=d:/github/fivewin" }
//"PRGFLAGS=-m -n -w3 -es2 -ge1 -DHB_VER_REVID=1" }
//"{mingw}CFLAGS=" + ;
//" -Wno-implicit-fallthrough" + ;
//" -Wno-cast-function-type" + ;
//" -Wno-misleading-indentation",
cPath := CheckFileOnPath( "hbmk2.exe" )
IF Empty( cPath )
ShowAlert( "hbmk2.exe not found on PATH to check hbmk.hbc" )
RETURN Nil
ENDIF
IF Right( cPath, 1 ) != "\"
cPath += "\"
ENDIF
cFileHbmk := cPath + "hbmk.hbc"
IF File( cFileHbmk )
cTxt := MemoRead( cFileHbmk )
IF Right( cTxt, 2 ) != hb_Eol()
cTxt += hb_Eol()
ENDIF
ENDIF
IF "xharbour" $ Lower( cPath )
AAdd( aParamList, "-xhb" )
ENDIF
FOR EACH cParam IN aParamList
IF ! cParam $ cTxt
cTxt += cParam + hb_Eol()
ShowAlert( "hbmk.hbc adding " + cParam )
stErro := .T.
lUpdate := .T.
ENDIF
NEXT
IF lUpdate
hb_MemoWrit( cFileHbmk, cTxt )
ENDIF
IF File( cPathLib + "libforcedebug.a" )
cLib1 := MemoRead( cPathLib + "libforcedebug.a" )
ENDIF
cLib2 := MemoRead( cPathLib + "libhbdebug.a" )
IF ! cLib1 == cLib2
ShowAlert( "libforcedebug.a not found or invalid" )
hb_MemoWrit( cPathLib + "libforcedebug.a", cLib2 )
ENDIF
RETURN Nil
STATIC FUNCTION CheckGetEnv( aCmdList )
LOCAL oElement
LOCAL aList := { ;
{ "HB_BUILD_DYN", "no" }, ;
{ "HB_BUILD_CONTRIB_DYN", "no" }, ;
{ "HB_BUILD_STRIP", "all" }, ;
{ "HB_BUILD_SHARED", "no" }, ;
{ "HB_COMPILER", "mingw" }, ;
{ "HB_INSTALL_PREFIX", "d:\harbour" }, ;
{ "HB_LANG", "EN" } }
// { "HB_USER_CFLAGS", "-DHB_GUI -HB_NO_PROFILER _HB_NO_TRACE" }
// { "HB_USER_CFLAGS", ["-Wno-implicit-fallthrough -Wno-reserved-identifier -Wno-used-but-marked-unused"] } }
//{ "HB_BUILD_MODE", "cpp" } }
// { "HB_USER_CFLAGS", "-Wno-null-dereference -Wno-cast-function-type -Wno-return-local-addr" } }
// win_Reg fail
FOR EACH oElement IN aList
IF Empty( win_RegRead( "HKCU\Environment\" + oElement[1] ) ) ;
.OR. ! win_RegRead( "HKCU\Environment\" + oElement[1] ) == oElement[2] ;
.OR. Empty( GetEnv( oElement[ 1 ] ) ) ;
.OR. ! GetEnv( oElement[ 1 ] ) == oElement[ 2 ]
ShowAlert( "Not found SET " + oElement[ 1 ] + "=" + oElement[ 2 ] )
ShellExecuteOpen( "setx", oElement[ 1 ] + " " + oElement[ 2 ],, 0 )
win_RegWrite( "HKCU\Environment\" + oElement[1], oElement[2] )
AAdd( aCmdList, "SET " + oElement[ 1 ] + " " + oElement[2] )
ENDIF
NEXT
RETURN Nil
STATIC FUNCTION CheckPath( aSignList, aCmdList )
LOCAL cPath, aPathList, lChanged := .F., cPathFile, cFile, cItem
cPath := win_RegRead( "HKCU\Environment\Path" )
hb_Default( @cPath, "" )
aPathList := hb_RegExSplit( ";", cPath )
FOR EACH cPath IN { ;
"d:\harbour\bin", ;
"d:\harbour\comp\mingw32\bin", ;
"d:\tools\util" }
IF ASCan( aPathList, { | e | Lower( cPath ) $ Lower( e ) } ) == 0
ShowAlert( "Adding PATH " + cPath )
AAdd( aPathList, cPath )
AAdd( aCmdList, "SET PATH=%PATH%;" + cPath )
lChanged := .T.
ENDIF
NEXT
IF lChanged
cPath := ""
FOR EACH cItem IN aPathList
cPath += iif( cItem:__EnumIndex() == 1, ";", "" ) + cItem
NEXT
win_RegWrite( "HKCU\Environment\Path", cPath )
// ShellExecuteOpen( "setx", [PATH "] + + cPath + ["] )
ENDIF
FOR EACH cFile IN { "c.bat" }
IF Empty( CheckFileOnPath( cFile ) )
ShowAlert( cFile + " not in PATH" )
ShowAlert( "Remember run build.exe as administrator from explorer, and re-open cmd window" )
ENDIF
NEXT
FOR EACH cFile IN { "hbmk2.exe", "harbour.exe", "upx.exe" } // "upx.exe", "mingw32-make.exe", "gcc.exe" }
cPathFile := CheckFileOnPath( cFile )
IF Empty( cPathFile )
ShowAlert( cFile + " not in PATH" )
ELSEIF ! TestSignedEXE( cPathFile + cFile )
AAdd( aSignList, cPathFile + cFile )
ShowAlert( cPathFile + cFile + " not signed" )
ENDIF
NEXT
RETURN Nil
STATIC FUNCTION CheckFileOnPath( cFile )
LOCAL cPath, cPathFile
cFile := Lower( cFile )
FOR EACH cPath IN hb_RegExSplit( ";", GetEnv( "PATH" ) )
IF Right( cPath, 1 ) != "\"
cPath := cPath + "\"
ENDIF
IF File( cPath + cFile )
cPathFile := cPath
EXIT
ENDIF
NEXT
RETURN cPathFile
STATIC FUNCTION CheckBuild()
LOCAL cDateTime, cTxt, cFileName, hSetup, cLastPath, aItem
LOCAL aList := { ;
{ "LastPath", "" }, ;
{ "LastBuildDate", "" }, ;
{ "Total", 0 }, ;
{ "Year", 0 }, ;
{ "Month", 0 }, ;
{ "Day", 0 } }
AAdd( aList, { Left( Dtos( Date() ), 4 ), 0 } )
AAdd( aList, { Left( Dtos( Date() ), 4 ) + " Max", 0 } )
AAdd( aList, { Left( Dtos( Date() ), 6 ), 0 } )
AAdd( aList, { Left( Dtos( Date() ), 6 ) + " Max", 0 } )
AAdd( aList, { Dtos( Date() ), 0 } )
cFileName := hb_FNameDir( hb_ProgName() ) + hb_FNameName( hb_ProgName() )
hSetup := hb_JsonDecode( MemoRead( cFileName + ".json" ) )
IF ValType( hSetup ) != "H"
hSetup := hb_Hash()
ENDIF
FOR EACH aItem IN aList
IF ! hb_HHasKey( hSetup, aItem[ 1 ] )
hb_HSet( hSetup, aItem[ 1 ], aItem[ 2 ] )
ENDIF
NEXT
cDateTime := Dtos( Date() ) + Substr( Time(), 1, 2 ) + Substr( Time(), 4, 2 )
cLastPath := hSetup[ "LastPath" ]
IF Day( Date() ) != Day( Stod( hSetup[ "LastBuildDate" ] ) )
hSetup[ "Day" ] := 0
ENDIF
IF Month( Date() ) != Month( Stod( hSetup[ "LastBuildDate" ] ) )
hSetup[ "Month" ] := 0
ENDIF
IF Year( Date() ) != Year( Stod( hSetup[ "LastBuildDate" ] ) )
hSetup[ "Year" ] := 0
ENDIF
hSetup[ "Total" ] += 1
hSetup[ "Year" ] += 1
hSetup[ "Month" ] += 1
hSetup[ "Day" ] += 1
hSetup[ "LastPath" ] := hb_cwd()
hSetup[ "LastBuildDate" ] := Dtos( Date() )
hSetup[ Left( Dtos( Date() ), 4 ) ] += 1
hSetup[ Left( Dtos( Date() ), 6 ) ] += 1
hSetup[ Dtos( Date() ) ] += 1
Reorganize( @hSetup )
hb_MemoWrit( cFileName + ".json", hb_JsonEncode( hSetup, .T. ) )
IF ! Upper( cLastPath ) == Upper( hb_cwd() )
DeleteContent( "c:\temp", .F. )
ENDIF
cTxt := "#define JOSEQUINTAS_VERSAO " + ["] + Transform( cDateTime, "@R 9999.99.99.9999" ) + ["] + hb_Eol()
cTxt += "#define JOSEQUINTAS_VERSAO_RC " + Transform( Substr( cDateTime, 3 ), "@R 9999,99,99,99" ) + hb_Eol()
hb_MemoWrit( cFileName + ".ch", cTxt )
IF File( "build.ch" ) // save on folder too, IF have it
hb_MemoWrit( "build.ch", cTxt )
ENDIF
AEval( Directory( "*.upx" ), { | e | fErase( e[ F_NAME ] ) } ) // temporary upx
RETURN Nil
STATIC FUNCTION DeleteContent( cFolder, lDeleteFolder )
LOCAL oFiles, oFile, lDelete
hb_Default( @lDeleteFolder, .F. )
ShowNormal( "Delete on " + cFolder )
oFiles := Directory( cFolder + "\*.*", "D" )
FOR EACH oFile IN oFiles
IF "D" $ oFile[ F_ATTR ]
IF oFile[ F_NAME ] != "." .AND. oFile[ F_NAME ] != ".."
DeleteContent( cFolder + "\" + oFile[ F_NAME ], .T. )
ENDIF
ELSE
lDelete := .T.
DO CASE
CASE hb_FNameExt( oFile[ F_NAME ] ) == ".c"
CASE hb_FNameExt( oFile[ F_NAME ] ) == ".o"
CASE ASCan( { ".PRG", ".DBF", ".CH", ".HBP", ".RC" }, { | e | e == Upper( hb_FNameExt( oFile[ F_NAME ] ) ) } ) != 0
lDelete := .F.
CASE oFile[ F_DATE ] != Date()
CASE Left( oFile[ F_TIME ], 4 ) != Left( Time(), 4 )
OTHERWISE
lDelete := .F.
ENDCASE
IF lDelete
ShowNormal( "Deleting " + cFolder + "\" + oFile[ F_NAME ] )
fErase( cFolder + "\" + oFile[ F_NAME ] )
ENDIF
ENDIF
NEXT
IF lDeleteFolder .AND. Len( Directory( cFolder + "\*.*" ) ) == 0
DirRemove( cFolder )
ENDIF
RETURN Nil
FUNCTION HB_GT_SYS()
REQUEST HB_GT_WVG_DEFAULT
RETURN Nil
FUNCTION AppVersaoExe()
RETURN ""
FUNCTION AppUserName()
RETURN ""
STATIC FUNCTION TestSignedEXE( cFileName )
LOCAL oSignedCode, lOk := .F.
BEGIN SEQUENCE WITH __BreakBlock()
oSignedCode := win_OleCreateObject( "CAPICOM.SignedCode" )
oSignedCode:FileName := cFileName
oSignedCode:Verify()
lOk := .T.
ENDSEQUENCE
IF ! lOk
ShowAlert( "Not installed CAPICOM and/or EXE not signed" )
IF .F.
SignEXE( cFileName )
ENDIF
ENDIF
RETURN lOk
STATIC FUNCTION ShowNormal( cText )
? cText
RETURN Nil
STATIC FUNCTION ShowAlert( cText )
LOCAL cSetColor := SetColor( "N/GR*" )
? cText
SetColor( cSetColor )
stErro := .T.
Inkey(2)
RETURN Nil
FUNCTION ShellExecuteOpen( cFileName, cParameters, cPath, nShow )
wapi_ShellExecute( Nil, "open", cFileName, cParameters, cPath, hb_DefaultValue( nShow, 1 ) )
RETURN Nil
PROCEDURE HB_GTSYS
REQUEST HB_GT_WVG_DEFAULT
RETURN
/*
ar x lib.a
objcopy --redefine-sym MsgYesNo=FWMsgYesNo nome.o
ar rcs lib.a *.o
#-ldflags=-Wl,--allow-multiple-definition -s -static
####SET HB_USER_LDFLAGS=-Wl,--allow-multiple-definition -s -static
*/
FUNCTION Reorganize( hSetup )
LOCAL dDatIni, dDatFim, dItem, nMax := -1, nQtd := 0, xName, xValue
dDatIni := Date() - Day( Date() ) + 1
dDatFim := Last_Day( dDatIni )
FOR dItem = dDatIni TO dDatFim
IF hb_HHasKey( hSetup, Dtos( dItem ) )
xValue := hSetup[ Dtos( dItem ) ]
nMax := Max( nMax, xValue )
nQtd += 1
ENDIF
NEXT
IF nQtd != 0
xName := hb_Dtoc( Date(), "YYYYMM" ) + " Max"
IF ! hb_HHasKey( hSetup, xName )
hb_HSet( hSetup, xName, nMax )
ELSE
hSetup[ xName ] := nMax
ENDIF
ENDIF
RETURN Nil
FUNCTION Last_Day( dData )
dData += ( 40 - Day( dData ) )
dData -= Day( dData )
RETURN dData
STATIC FUNCTION SignEXE( cFileName )
LOCAL oSignedCode, oSigner, lOk := .F.
BEGIN SEQUENCE WITH __BreakBlock()
oSignedCode := win_OleCreateObject( "CAPICOM.SignedCode" )
oSignedCode:FileName := cFileName
oSigner := win_OleCreateObject( "CAPICOM.Signer" )
oSigner:Certificate := CAPICOMCertificado( "JPA Tecnologia Ltda SN" )
oSignedCode:Sign( oSigner )
oSignedCode:TimeStamp( "http://timestamp.digicert.com" )
lOk := .T.
ENDSEQUENCE
IF ! lOk
ShowAlert( "Not installed CAPICOM and/or EXE not signed" )
ENDIF
RETURN lOk
Tem também um detalhe de resource embutido.
#pragma begin dump
Quando altera o arquivo externo, o fonte não é recompilado, então coloquei no build pra apagar o OBJ.
Também cria um log pra curiosidade/estatística
Código: Selecionar todos
{
"LastPath": "d:\\fontes\\integra\\",
"LastBuildDate": "20250216",
"Day": 51,
"Year": 2034,
"Month": 753,
"Total": 10962,
"2024": 8928,
"202409": 1307,
"202409 Max": 230,
"202410": 1249,
"202410 Max": 111,
"202411": 1257,
"202411 Max": 141,
"202412": 824,
"202412 Max": 106,
"2025": 2034,
"2025 Max": 0,
"202501": 1281,
"202501 Max": 187,
"202502": 753,
"202502 Max": 127,
"20250201": 17,
"20250203": 97,
"20250204": 35,
"20250205": 127,
"20250206": 52,
"20250207": 36,
"20250209": 47,
"20250210": 8,
"20250211": 46,
"20250212": 38,
"20250213": 111,
"20250214": 88,
"20250216": 51
}
Difícil imaginar tanta compilação assim, só mesmo com contador registrando.
Outra coisa que o build.prg cria é o build.ch
Código: Selecionar todos
#define JOSEQUINTAS_VERSAO "2025.02.16.1944"
#define JOSEQUINTAS_VERSAO_RC 2502,16,19,44
Com isso, o aplicativo tem número de versão exclusivo pra cada compilação, usando data/hora.
Tem também o debug do harbour forçado, pra não entrar da hwgui, minigui ou fivewin.
Renomeei o arquivo original, é a única forma de acrescentar sem reclamação.
Testo no build.prg também, inclusive se é a versão correta, se o conteúdo bate com o debug original.
Tudo coisa relativamente simples, apenas fui acrescentando pra reduzir o trabalho braçal, ou pra não esquecer de fazer, como o debug.