* harbour/include/hbcomp.h
* harbour/include/hbcompdf.h
* harbour/source/compiler/hbmain.c
* harbour/source/compiler/cmdcheck.c
* harbour/source/compiler/hbcomp.c
* harbour/source/compiler/genc.c
* harbour/source/compiler/harbour.yyc
* harbour/source/compiler/harbour.y
* harbour/source/compiler/hbdbginf.c
* harbour/source/compiler/genhrb.c
* harbour/source/compiler/gencobj.c
- removed old "AutoOpen" code used for @<name>.clp, SET PROCEDURE TO ...
and DO <func> [ WITH <args,...> ] statements
It was neither Clipper compatible not working correctly in some cases.
+ added new code for multi .prg module compilation into single unit
with support for multiple file wide declarations just like Cipper
does doe @.clp files and SET PROCEDURE TO / DO ... [ WITH ... ]
% cleaned redundant code used in harbour compiler to execute grammar
parser with different conditions. Now it's much shorter and simpler
and hb_compparse() is called only from one place.
! fixed possible multiple declarations in symbol table for ANNOUNCE
function
% rewritten C code generation to increase speed and make it independent
from some internal compiler structures. Now it's shorter and faster
but it strongly uses scope attributes in symbol table so they have
to be properly set during compilation and new code for .c file
generation should help in their validation.
% few other optimizations and cleanups
Now Harbour can compile code like:
/*** t01.prg ***/
static s_var := "main sVar"
proc main()
? procname(), "->", sVar
do t02
return
/*** t02.prg ***/
static s_var := "t02 sVar"
proc t02()
? procname(), "->", sVar
return
by simpe:
harbour -n -w -es2 t01
or using tst.clp:
t01
t02
and:
harbour -n -w -es2 @tst
in both cases it works just like Clipper. Please note that in the
second version generated file inherits (like in Clipper) name from
.clp file and is called "tst.c". The .clp file name is also used
as module handler signature.
TODO: add support for multiple static functions with the same name
but coming from different .prg modules compiled into single
unit using @.clp files or SET PROCEDURE TO / DO ... [ WITH ... ]
In above modifications I already implemented it partially but
I haven't made one very important extension which strongly
interacts with symbol table usage during compilation and can
be source of really bad problems if I made sth wrong so I plan
to finish when above changes will have been tested.
343 lines
10 KiB
C
343 lines
10 KiB
C
/*
|
|
* $Id$
|
|
*/
|
|
|
|
/*
|
|
* Harbour Project source code:
|
|
* Native compiler object module generation from Harbour C output.
|
|
*
|
|
* Copyright 2001 Jose Lalin <dezac@corevia.com>
|
|
* www - http://www.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/).
|
|
*
|
|
*/
|
|
|
|
#include "hbcomp.h"
|
|
#include "hb_io.h"
|
|
|
|
#ifdef HB_LEGACY_LEVEL2
|
|
|
|
#define HB_CFG_FILENAME "harbour.cfg"
|
|
|
|
/* QUESTION: Allocate buffer dynamically ? */
|
|
#define HB_CFG_LINE_LEN ( ( HB_PATH_MAX - 1 ) << 1 )
|
|
|
|
#if defined( HB_OS_UNIX )
|
|
#define HB_NULL_STR " > /dev/null"
|
|
#else
|
|
#define HB_NULL_STR " >nul"
|
|
#endif
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
|
|
static char * hb_searchpath( const char * pszFile, char * pszEnv, char * pszCfg )
|
|
{
|
|
char * pszPath;
|
|
BOOL bFound = FALSE;
|
|
|
|
/* Check current dir first */
|
|
if( hb_fsFileExists( ( const char * ) pszFile ) )
|
|
{
|
|
hb_snprintf( pszCfg, HB_PATH_MAX, "%s", pszFile );
|
|
return ( char * ) pszFile;
|
|
}
|
|
else
|
|
{
|
|
/* Check if pszFile exists somewhere in the path */
|
|
while( * pszEnv )
|
|
{
|
|
pszPath = pszEnv;
|
|
while( *pszEnv )
|
|
{
|
|
if( *pszEnv == HB_OS_PATH_LIST_SEP_CHR )
|
|
{
|
|
*pszEnv++ = '\0';
|
|
break;
|
|
}
|
|
pszEnv++;
|
|
}
|
|
if( *pszPath )
|
|
{
|
|
hb_snprintf( pszCfg, HB_PATH_MAX, "%s%c%s", pszPath, HB_OS_PATH_DELIM_CHR, pszFile );
|
|
if( hb_fsFileExists( ( const char * ) pszCfg ) )
|
|
{
|
|
bFound = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* If not found, make sure to return a NULL string */
|
|
if( ! bFound )
|
|
*pszCfg = '\0';
|
|
|
|
return ( char * ) pszCfg;
|
|
}
|
|
|
|
static void hb_substenvvar( char * szLine )
|
|
{
|
|
char szBuf[ HB_CFG_LINE_LEN + 1 ], * szVar, * ptr;
|
|
|
|
ptr = szLine;
|
|
while( *ptr )
|
|
{
|
|
if( *ptr == '$' && ptr[ 1 ] == '(' )
|
|
{
|
|
int i = 2, j;
|
|
while( ( ptr[ i ] >= 'A' && ptr[ i ] <= 'Z' ) ||
|
|
( ptr[ i ] >= 'a' && ptr[ i ] <= 'z' ) ||
|
|
( ptr[ i ] >= '0' && ptr[ i ] <= '0' ) || ptr[ i ] == '_' )
|
|
++i;
|
|
if( i > 2 && ptr[ i ] == ')' )
|
|
{
|
|
ptr[ 0 ] = 0;
|
|
ptr[ i ] = 0;
|
|
hb_strncpy( szBuf, szLine, sizeof( szBuf ) - 1 );
|
|
szVar = hb_getenv( ptr + 2 );
|
|
if( szVar )
|
|
{
|
|
hb_strncat( szBuf, szVar, sizeof( szBuf ) - 1 );
|
|
hb_xfree( szVar );
|
|
}
|
|
j = strlen( szBuf );
|
|
hb_strncat( szBuf, &ptr[ i + 1 ], sizeof( szBuf ) - 1 );
|
|
hb_strncpy( szLine, szBuf, HB_CFG_LINE_LEN );
|
|
ptr = szLine + j;
|
|
}
|
|
}
|
|
++ptr;
|
|
}
|
|
}
|
|
|
|
/* Builds platform dependant object module from Harbour C output */
|
|
void hb_compGenCObj( HB_COMP_DECL, PHB_FNAME pFileName )
|
|
{
|
|
char szFileName[ HB_PATH_MAX ];
|
|
char szLine[ HB_CFG_LINE_LEN + 1 ];
|
|
char szCompiler[ HB_CFG_LINE_LEN + 1 ] = "";
|
|
char szOptions[ HB_CFG_LINE_LEN + 1 ] = "";
|
|
char szCommandLine[ HB_CFG_LINE_LEN * 2 + 1 ];
|
|
char szOutPath[ HB_PATH_MAX ] = "\0";
|
|
char pszTemp[ HB_PATH_MAX ] = "";
|
|
char buffer[ HB_CFG_LINE_LEN * 2 + 1024 ];
|
|
#if defined( HB_OS_UNIX )
|
|
char * pszEnv = hb_strdup( "/etc:/usr/local/etc" );
|
|
#else
|
|
char * pszEnv = hb_getenv( "PATH" );
|
|
#endif
|
|
char * pszCfgFileName = hb_getenv( "HB_CFG_FILE" );
|
|
FILE * filecfg;
|
|
BOOL bVerbose = FALSE; /* Don't show C compiler messages (default). */
|
|
BOOL bDelTmp = TRUE; /* Delete intermediate C file (default). */
|
|
int iSuccess;
|
|
|
|
HB_SYMBOL_UNUSED( HB_COMP_PARAM );
|
|
|
|
/* First pass: build the C output */
|
|
|
|
/* Force file extension to avoid collisions when called from a make utility */
|
|
pFileName->szExtension = ".c";
|
|
hb_fsFNameMerge( szFileName, pFileName );
|
|
hb_compGenCCode( HB_COMP_PARAM, pFileName );
|
|
|
|
/* Begin second pass */
|
|
|
|
/* Set up things */
|
|
if( !pszCfgFileName )
|
|
pszCfgFileName = hb_strdup( HB_CFG_FILENAME );
|
|
|
|
if( pszEnv && *hb_searchpath( pszCfgFileName, pszEnv, pszTemp ) )
|
|
{
|
|
filecfg = hb_fopen( pszTemp, "rt" );
|
|
if( ! filecfg )
|
|
{
|
|
hb_compGenError( HB_COMP_PARAM, hb_comp_szErrors, 'E', HB_COMP_ERR_OPEN_CFG, szFileName, NULL );
|
|
if( pszEnv )
|
|
hb_xfree( ( void * ) pszEnv );
|
|
hb_xfree( ( void * ) pszCfgFileName );
|
|
return;
|
|
}
|
|
|
|
while( fgets( szLine, HB_CFG_LINE_LEN, filecfg ) != NULL )
|
|
{
|
|
ULONG ulLen;
|
|
char * szStr = szLine;
|
|
char * szToken;
|
|
|
|
hb_substenvvar( szLine );
|
|
|
|
/* Trim left */
|
|
while( HB_ISSPACE( *szStr ) )
|
|
szStr++;
|
|
|
|
/* Trim right */
|
|
ulLen = strlen( szStr );
|
|
while( ulLen && HB_ISSPACE( szStr[ ulLen - 1 ] ) )
|
|
ulLen--;
|
|
|
|
szStr[ ulLen ] = '\0';
|
|
/* TODO: Check for comments within macros, i.e: CC=bcc #comment */
|
|
|
|
if( *szStr )
|
|
{
|
|
szToken = strchr( szStr, '=' );
|
|
|
|
if( szToken )
|
|
{
|
|
*szToken++ = '\0';
|
|
if( *szToken )
|
|
{
|
|
/* Checks compiler name */
|
|
if( ! hb_stricmp( szStr, "CC" ) )
|
|
{
|
|
hb_snprintf( szCompiler, sizeof( szCompiler ), "%s", szToken );
|
|
}
|
|
/* Checks optional switches */
|
|
else if( ! hb_stricmp( szStr, "CFLAGS" ) )
|
|
{
|
|
hb_snprintf( szOptions, sizeof( szCompiler ), "%s", szToken );
|
|
}
|
|
/* Wanna see C compiler output ? */
|
|
else if( ! hb_stricmp( szStr, "VERBOSE" ) )
|
|
{
|
|
if( ! hb_stricmp( szToken, "YES" ) )
|
|
bVerbose = TRUE;
|
|
}
|
|
/* Delete intermediate C file ? */
|
|
else if( ! hb_stricmp( szStr, "DELTMP" ) )
|
|
{
|
|
if( ! hb_stricmp( szToken, "NO" ) )
|
|
bDelTmp = FALSE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fclose( filecfg );
|
|
}
|
|
else
|
|
{
|
|
hb_snprintf( buffer, sizeof( buffer ),
|
|
"\nError: Can't find %s file in %s.\n"
|
|
"%s should be a text file that contains:\n"
|
|
"CC=C compiler binary name eg. CC=gcc\n"
|
|
"CFLAGS=C compiler options eg. -c -I<includes>\n"
|
|
" ( 'compile only' and harbour include dir are mandatory )\n"
|
|
"VERBOSE=NO|YES to show steps messages default is NO\n"
|
|
"DELTMP=NO|YES to delete generated C source default is YES\n"
|
|
"remember also to properly set the C compiler env.\n",
|
|
pszCfgFileName, pszEnv, pszCfgFileName );
|
|
hb_compOutStd( HB_COMP_PARAM, buffer );
|
|
|
|
if( pszEnv )
|
|
hb_xfree( ( void * ) pszEnv );
|
|
hb_xfree( ( void * ) pszCfgFileName );
|
|
return;
|
|
}
|
|
|
|
if( pszEnv )
|
|
hb_xfree( ( void * ) pszEnv );
|
|
|
|
if( ! HB_COMP_PARAM->fQuiet )
|
|
{
|
|
hb_snprintf( buffer, sizeof( buffer ),
|
|
"\nBuilding object module for \'%s\'\nusing C compiler \'%s\' as defined in \'%s\'...\n",
|
|
szFileName, szCompiler, pszCfgFileName );
|
|
hb_compOutStd( HB_COMP_PARAM, buffer );
|
|
}
|
|
|
|
/* Check if -o<path> was used */
|
|
if( HB_COMP_PARAM->pOutPath )
|
|
{
|
|
PHB_FNAME pOut = hb_fsFNameSplit( ( char * ) szFileName );
|
|
|
|
if( HB_COMP_PARAM->pOutPath->szPath )
|
|
pOut->szPath = HB_COMP_PARAM->pOutPath->szPath;
|
|
|
|
#if defined( __BORLANDC__ ) || defined( _MSC_VER ) || defined( __WATCOMC__ )
|
|
pOut->szExtension = ".obj";
|
|
#else
|
|
pOut->szExtension = ".o"; /* Don't know if we can hardcode it for Un*x */
|
|
#endif
|
|
hb_fsFNameMerge( pszTemp, pOut );
|
|
|
|
#if defined( _MSC_VER )
|
|
hb_strncat( szOutPath, "-Fo", sizeof( szOutPath ) - 1 );
|
|
#elif defined( __WATCOMC__ )
|
|
hb_strncat( szOutPath, "-fo=", sizeof( szOutPath ) - 1 );
|
|
#else
|
|
hb_strncat( szOutPath, "-o", sizeof( szOutPath ) - 1 );
|
|
#endif
|
|
|
|
hb_strncat( szOutPath, pszTemp, sizeof( szOutPath ) - 1 );
|
|
|
|
hb_xfree( pOut );
|
|
}
|
|
|
|
if( *szCompiler )
|
|
{
|
|
hb_snprintf( szCommandLine, sizeof( szCommandLine ), "%s %s %s %s", szCompiler, szOptions, szOutPath, szFileName );
|
|
|
|
if( bVerbose )
|
|
{
|
|
hb_snprintf( buffer, sizeof( buffer ), "Exec: %s\n", szCommandLine );
|
|
hb_compOutStd( HB_COMP_PARAM, buffer );
|
|
}
|
|
else
|
|
hb_strncat( szCommandLine, HB_NULL_STR, sizeof( szCommandLine ) - 1 );
|
|
|
|
/* Compile it! */
|
|
iSuccess = ( system( szCommandLine ) != -1 );
|
|
|
|
/* Delete intermediate .c file */
|
|
/* QUESTION: Leave this file if C compiler fails ? */
|
|
if( bDelTmp ) /* && iSuccess ) */
|
|
{
|
|
if( bVerbose )
|
|
{
|
|
hb_snprintf( buffer, sizeof( buffer ), "Deleting: \"%s\"\n", szFileName );
|
|
hb_compOutStd( HB_COMP_PARAM, buffer );
|
|
}
|
|
remove( ( char * ) szFileName );
|
|
}
|
|
|
|
if( ! HB_COMP_PARAM->fQuiet )
|
|
{
|
|
if( iSuccess )
|
|
hb_compOutStd( HB_COMP_PARAM, "Done.\n" );
|
|
else
|
|
{
|
|
hb_snprintf( buffer, sizeof( buffer ),
|
|
"\nFailed to execute: \"%s\"\n", szCommandLine );
|
|
hb_compOutErr( HB_COMP_PARAM, buffer );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hb_snprintf( buffer, sizeof( buffer ),
|
|
"\nError: No compiler defined in %s\n", pszCfgFileName );
|
|
hb_compOutErr( HB_COMP_PARAM, buffer );
|
|
}
|
|
|
|
hb_xfree( ( void * ) pszCfgFileName );
|
|
}
|
|
|
|
#endif
|