689 lines
18 KiB
C
689 lines
18 KiB
C
/*
|
|
* TCC - Tiny C Compiler
|
|
*
|
|
* Copyright (c) 2001-2004 Fabrice Bellard
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library 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
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include "config.h"
|
|
#endif /* HAVE_CONFIG_H */
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <tcc.h>
|
|
#include <tcc/path.h>
|
|
#include <tcc/memory.h>
|
|
#include <tcc/object.h>
|
|
#include "cc/cc.h"
|
|
|
|
/********************************************************/
|
|
/* global variables */
|
|
|
|
/* XXX: get rid of this ASAP (or maybe not) */
|
|
struct TCCState *tcc_state;
|
|
/* an array of pointers to memory to be free'd after errors */
|
|
void** stk_data;
|
|
int nb_stk_data;
|
|
|
|
/********************************************************/
|
|
|
|
PUB_FUNC void tcc_enter_state(TCCState *s1)
|
|
{
|
|
if (s1->error_set_jmp_enabled)
|
|
return;
|
|
tcc_state = s1;
|
|
}
|
|
|
|
PUB_FUNC void tcc_exit_state(TCCState *s1)
|
|
{
|
|
if (s1->error_set_jmp_enabled)
|
|
return;
|
|
tcc_state = NULL;
|
|
}
|
|
|
|
char *
|
|
tcc_load_text(int fd)
|
|
{
|
|
int len;
|
|
char *buf;
|
|
|
|
len = lseek(fd, 0, SEEK_END);
|
|
buf = load_data(fd, 0, len + 1); /** XXX: rename func */
|
|
if (buf)
|
|
{
|
|
buf[len] = 0;
|
|
}
|
|
|
|
return (buf);
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
# define realpath(file, buf) _fullpath(buf, file, 260)
|
|
#endif
|
|
|
|
/* for #pragma once */
|
|
int normalized_PATHCMP(const char *f1, const char *f2)
|
|
{
|
|
char *p1, *p2;
|
|
int ret = 1;
|
|
if (!!(p1 = realpath(f1, NULL))) {
|
|
if (!!(p2 = realpath(f2, NULL))) {
|
|
ret = PATHCMP(p1, p2);
|
|
free(p2); /* realpath() requirement */
|
|
}
|
|
free(p1);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/********************************************************/
|
|
/* dynarrays */
|
|
|
|
void dynarray_add(void *ptab, int *nb_ptr, void *data)
|
|
{
|
|
int nb, nb_alloc;
|
|
void **pp;
|
|
|
|
nb = *nb_ptr;
|
|
pp = *(void ***)ptab;
|
|
/* every power of two we double array size */
|
|
if ((nb & (nb - 1)) == 0) {
|
|
if (!nb)
|
|
nb_alloc = 1;
|
|
else
|
|
nb_alloc = nb * 2;
|
|
pp = tcc_realloc(pp, nb_alloc * sizeof(void *));
|
|
*(void***)ptab = pp;
|
|
}
|
|
pp[nb++] = data;
|
|
*nb_ptr = nb;
|
|
}
|
|
|
|
void dynarray_reset(void *pp, int *n)
|
|
{
|
|
void **p;
|
|
for (p = *(void***)pp; *n; ++p, --*n)
|
|
if (*p)
|
|
tcc_free(*p);
|
|
tcc_free(*(void**)pp);
|
|
*(void**)pp = NULL;
|
|
}
|
|
|
|
static void tcc_split_path(TCCState *s, void *p_ary, int *p_nb_ary, const char *in)
|
|
{
|
|
const char *p;
|
|
do {
|
|
int c;
|
|
CString str;
|
|
|
|
cstr_new(&str);
|
|
for (p = in; c = *p, c != '\0' && c != PATHSEP[0]; ++p) {
|
|
if (c == '{' && p[1] && p[2] == '}') {
|
|
c = p[1], p += 2;
|
|
if (c == 'B')
|
|
cstr_cat(&str, s->tcc_lib_path, -1);
|
|
if (c == 'R')
|
|
cstr_cat(&str, CONFIG_SYSROOT, -1);
|
|
if (c == 'f' && file) {
|
|
/* substitute current file's dir */
|
|
const char *f = file->true_filename;
|
|
const char *b = tcc_basename(f);
|
|
if (b > f)
|
|
cstr_cat(&str, f, b - f - 1);
|
|
else
|
|
cstr_cat(&str, ".", 1);
|
|
}
|
|
} else {
|
|
cstr_ccat(&str, c);
|
|
}
|
|
}
|
|
if (str.size) {
|
|
cstr_ccat(&str, '\0');
|
|
dynarray_add(p_ary, p_nb_ary, tcc_strdup(str.data));
|
|
}
|
|
cstr_free(&str);
|
|
in = p+1;
|
|
} while (*p);
|
|
}
|
|
|
|
/********************************************************/
|
|
/* warning / error */
|
|
|
|
/* warn_... option bits */
|
|
#define WARN_ON 1 /* warning is on (-Woption) */
|
|
#define WARN_ERR 2 /* warning is an error (-Werror=option) */
|
|
#define WARN_NOE 4 /* warning is not an error (-Wno-error=option) */
|
|
|
|
/* error1() modes */
|
|
enum { ERROR_WARN, ERROR_NOABORT, ERROR_ERROR };
|
|
|
|
static void error1(int mode, const char *fmt, va_list ap)
|
|
{
|
|
BufferedFile **pf, *f;
|
|
TCCState *s1 = tcc_state;
|
|
CString cs;
|
|
int line = 0;
|
|
|
|
tcc_exit_state(s1);
|
|
|
|
if (mode == ERROR_WARN) {
|
|
if (s1->warn_error)
|
|
mode = ERROR_ERROR;
|
|
if (s1->warn_num) {
|
|
/* handle tcc_warning_c(warn_option)(fmt, ...) */
|
|
int wopt = *(&s1->warn_none + s1->warn_num);
|
|
s1->warn_num = 0;
|
|
if (0 == (wopt & WARN_ON))
|
|
return;
|
|
if (wopt & WARN_ERR)
|
|
mode = ERROR_ERROR;
|
|
if (wopt & WARN_NOE)
|
|
mode = ERROR_WARN;
|
|
}
|
|
if (s1->warn_none)
|
|
return;
|
|
}
|
|
|
|
cstr_new(&cs);
|
|
if (fmt[0] == '%' && fmt[1] == 'i' && fmt[2] == ':')
|
|
line = va_arg(ap, int), fmt += 3;
|
|
f = NULL;
|
|
if (s1->error_set_jmp_enabled) { /* we're called while parsing a file */
|
|
/* use upper file if inline ":asm:" or token ":paste:" */
|
|
for (f = file; f && f->filename[0] == ':'; f = f->prev)
|
|
;
|
|
}
|
|
if (f) {
|
|
for(pf = s1->include_stack; pf < s1->include_stack_ptr; pf++)
|
|
cstr_printf(&cs, "In file included from %s:%d:\n",
|
|
(*pf)->filename, (*pf)->line_num - 1);
|
|
if (0 == line)
|
|
line = f->line_num - ((tok_flags & TOK_FLAG_BOL) && !macro_ptr);
|
|
cstr_printf(&cs, "%s:%d: ", f->filename, line);
|
|
} else if (s1->current_filename) {
|
|
cstr_printf(&cs, "%s: ", s1->current_filename);
|
|
} else {
|
|
cstr_printf(&cs, "tcc: ");
|
|
}
|
|
cstr_printf(&cs, mode == ERROR_WARN ? "warning: " : "error: ");
|
|
if (pp_expr > 1)
|
|
pp_error(&cs); /* special handler for preprocessor expression errors */
|
|
else
|
|
cstr_vprintf(&cs, fmt, ap);
|
|
if (!s1->error_func) {
|
|
/* default case: stderr */
|
|
if (s1 && s1->output_type == TCC_OUTPUT_PREPROCESS && s1->ppfp == stdout)
|
|
printf("\n"); /* print a newline during tcc -E */
|
|
fflush(stdout); /* flush -v output */
|
|
fprintf(stderr, "%s\n", (char*)cs.data);
|
|
fflush(stderr); /* print error/warning now (win32) */
|
|
} else {
|
|
s1->error_func(s1->error_opaque, (char*)cs.data);
|
|
}
|
|
cstr_free(&cs);
|
|
if (mode != ERROR_WARN)
|
|
s1->nb_errors++;
|
|
if (mode == ERROR_ERROR && s1->error_set_jmp_enabled) {
|
|
while (nb_stk_data)
|
|
tcc_free(*(void**)stk_data[--nb_stk_data]);
|
|
longjmp(s1->error_jmp_buf, 1);
|
|
}
|
|
}
|
|
|
|
void tcc_set_error_func(TCCState *s, void *error_opaque, TCCErrorFunc *error_func)
|
|
{
|
|
s->error_opaque = error_opaque;
|
|
s->error_func = error_func;
|
|
}
|
|
|
|
/* error without aborting current compilation */
|
|
PUB_FUNC int _tcc_error_noabort(const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
va_start(ap, fmt);
|
|
error1(ERROR_NOABORT, fmt, ap);
|
|
va_end(ap);
|
|
return -1;
|
|
}
|
|
|
|
#undef _tcc_error
|
|
PUB_FUNC void _tcc_error(const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
va_start(ap, fmt);
|
|
error1(ERROR_ERROR, fmt, ap);
|
|
exit(1);
|
|
}
|
|
#define _tcc_error use_tcc_error_noabort
|
|
|
|
PUB_FUNC void _tcc_warning(const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
va_start(ap, fmt);
|
|
error1(ERROR_WARN, fmt, ap);
|
|
va_end(ap);
|
|
}
|
|
|
|
|
|
/********************************************************/
|
|
/* I/O layer */
|
|
|
|
static int _tcc_open(TCCState *s1, const char *filename)
|
|
{
|
|
int fd;
|
|
if (strcmp(filename, "-") == 0)
|
|
fd = 0, filename = "<stdin>";
|
|
else
|
|
fd = open(filename, O_RDONLY | O_BINARY);
|
|
if ((s1->verbose == 2 && fd >= 0) || s1->verbose == 3)
|
|
printf("%s %*s%s\n", fd < 0 ? "nf":"->",
|
|
(int)(s1->include_stack_ptr - s1->include_stack), "", filename);
|
|
return fd;
|
|
}
|
|
|
|
int tcc_open(TCCState *s1, const char *filename)
|
|
{
|
|
int fd = _tcc_open(s1, filename);
|
|
if (fd < 0)
|
|
return -1;
|
|
tcc_open_bf(s1, filename, 0);
|
|
file->fd = fd;
|
|
return 0;
|
|
}
|
|
|
|
/* compile the file opened in 'file'. Return non zero if errors. */
|
|
static int
|
|
tcc_compile(TCCState *s1, int filetype, const char *str, int fd)
|
|
{
|
|
/* Here we enter the code section where we use the global variables for
|
|
parsing and code generation (tccpp.c, tccgen.c, <target>-gen.c).
|
|
Other threads need to wait until we're done.
|
|
|
|
Alternatively we could use thread local storage for those global
|
|
variables, which may or may not have advantages */
|
|
int len;
|
|
|
|
tcc_enter_state(s1);
|
|
s1->error_set_jmp_enabled = 1;
|
|
|
|
if (setjmp(s1->error_jmp_buf) == 0)
|
|
{
|
|
s1->nb_errors = 0;
|
|
|
|
if (fd == -1)
|
|
{
|
|
len = strlen(str);
|
|
tcc_open_bf(s1, "<string>", len);
|
|
memcpy(file->buffer, str, len);
|
|
}
|
|
else
|
|
{
|
|
tcc_open_bf(s1, str, 0);
|
|
file->fd = fd;
|
|
}
|
|
|
|
preprocess_start(s1, filetype);
|
|
tccgen_init(s1);
|
|
|
|
if (s1->output_type == TCC_OUTPUT_PREPROCESS)
|
|
{
|
|
tcc_preprocess(s1);
|
|
}
|
|
else
|
|
{
|
|
coff_begin_file(s1);
|
|
if (filetype & (AFF_TYPE_ASM | AFF_TYPE_ASMPP))
|
|
{
|
|
tcc_assemble(s1, !!(filetype & AFF_TYPE_ASMPP));
|
|
}
|
|
else
|
|
{
|
|
tccgen_compile(s1);
|
|
}
|
|
coff_end_file(s1);
|
|
}
|
|
}
|
|
tccgen_finish(s1);
|
|
preprocess_end(s1);
|
|
s1->error_set_jmp_enabled = 0;
|
|
tcc_exit_state(s1);
|
|
return (s1->nb_errors != 0 ? -1 : 0);
|
|
}
|
|
|
|
/* define a preprocessor symbol. value can be NULL, sym can be "sym=val" */
|
|
void tcc_define_symbol(TCCState *s1, const char *sym, const char *value)
|
|
{
|
|
const char *eq;
|
|
if (NULL == (eq = strchr(sym, '=')))
|
|
eq = strchr(sym, 0);
|
|
if (NULL == value)
|
|
value = *eq ? eq + 1 : "1";
|
|
cstr_printf(&s1->cmdline_defs, "#define %.*s %s\n", (int)(eq-sym), sym, value);
|
|
}
|
|
|
|
/* undefine a preprocessor symbol */
|
|
void tcc_undefine_symbol(TCCState *s1, const char *sym)
|
|
{
|
|
cstr_printf(&s1->cmdline_defs, "#undef %s\n", sym);
|
|
}
|
|
|
|
|
|
TCCState *tcc_new(void)
|
|
{
|
|
TCCState *s;
|
|
|
|
s = tcc_mallocz(sizeof(TCCState));
|
|
#ifdef TCC_MEMORY_DEBUG
|
|
tcc_memcheck(1);
|
|
#endif
|
|
|
|
#undef gnu_ext
|
|
s->gnu_ext = 1;
|
|
s->tcc_ext = 1;
|
|
s->dollars_in_identifiers = 1; /*on by default like in gcc/clang*/
|
|
s->cversion = 199901; /* default unless -std=c11 is supplied */
|
|
s->warn_implicit_function_declaration = 1;
|
|
s->warn_discarded_qualifiers = 1;
|
|
s->ms_extensions = 1;
|
|
|
|
#ifdef __CHAR_UNSIGNED__
|
|
s->char_is_unsigned = 1;
|
|
#endif
|
|
|
|
s->ppfp = stdout;
|
|
/* might be used in error() before preprocess_start() */
|
|
s->include_stack_ptr = s->include_stack;
|
|
|
|
tcc_set_lib_path(s, CONFIG_TCCDIR);
|
|
return s;
|
|
}
|
|
|
|
void
|
|
tcc_delete(TCCState *s1)
|
|
{
|
|
/* free sections */
|
|
coff_delete(s1);
|
|
|
|
/* free library paths */
|
|
dynarray_reset(&s1->library_paths, &s1->nb_library_paths);
|
|
dynarray_reset(&s1->crt_paths, &s1->nb_crt_paths);
|
|
|
|
/* free include paths */
|
|
dynarray_reset(&s1->include_paths, &s1->nb_include_paths);
|
|
dynarray_reset(&s1->sysinclude_paths, &s1->nb_sysinclude_paths);
|
|
|
|
tcc_free(s1->tcc_lib_path);;
|
|
tcc_free(s1->entryname);
|
|
tcc_free(s1->outfile);
|
|
tcc_free(s1->deps_outfile);
|
|
dynarray_reset(&s1->files, &s1->nb_files);
|
|
dynarray_reset(&s1->target_deps, &s1->nb_target_deps);
|
|
dynarray_reset(&s1->pragma_libs, &s1->nb_pragma_libs);
|
|
dynarray_reset(&s1->argv, &s1->argc);
|
|
cstr_free(&s1->cmdline_defs);
|
|
cstr_free(&s1->cmdline_incl);
|
|
cstr_free(&s1->linker_arg);
|
|
tcc_free(s1);
|
|
#ifdef TCC_MEMORY_DEBUG
|
|
tcc_memcheck(-1);
|
|
#endif
|
|
}
|
|
|
|
int
|
|
tcc_set_output_type(TCCState *s, int output_type)
|
|
{
|
|
s->output_type = output_type;
|
|
|
|
if (!s->nostdinc) {
|
|
/* default include paths */
|
|
/* -isystem paths have already been handled */
|
|
tcc_add_sysinclude_path(s, CONFIG_TCC_SYSINCLUDEPATHS);
|
|
}
|
|
|
|
if (output_type == TCC_OUTPUT_PREPROCESS) {
|
|
return 0;
|
|
}
|
|
|
|
/* add sections */
|
|
coff_new(s);
|
|
|
|
if (output_type == TCC_OUTPUT_OBJ) {
|
|
/* always elf for objects */
|
|
s->output_format = TCC_OUTPUT_FORMAT_COFF;
|
|
return 0;
|
|
}
|
|
|
|
tcc_add_library_path(s, CONFIG_TCC_LIBPATHS);
|
|
|
|
/* paths for crt objects */
|
|
tcc_split_path(s, &s->crt_paths, &s->nb_crt_paths, CONFIG_TCC_CRTPREFIX);
|
|
if (!s->nostdlib)
|
|
coff_add_crtbegin(s);
|
|
return 0;
|
|
}
|
|
|
|
int tcc_add_include_path(TCCState *s, const char *pathname)
|
|
{
|
|
tcc_split_path(s, &s->include_paths, &s->nb_include_paths, pathname);
|
|
return 0;
|
|
}
|
|
|
|
int tcc_add_sysinclude_path(TCCState *s, const char *pathname)
|
|
{
|
|
tcc_split_path(s, &s->sysinclude_paths, &s->nb_sysinclude_paths, pathname);
|
|
return 0;
|
|
}
|
|
|
|
static int guess_filetype(const char *filename);
|
|
|
|
int
|
|
tcc_add_file_internal(TCCState *s1, const char *filename, int flags)
|
|
{
|
|
COFFFileHeader fhdr;
|
|
int obj_type;
|
|
int fd, ret = -1;
|
|
|
|
if (0 == (flags & AFF_TYPE_MASK))
|
|
{
|
|
flags |= guess_filetype(filename);
|
|
}
|
|
|
|
/* ignore binary files with -E */
|
|
if (s1->output_type == TCC_OUTPUT_PREPROCESS
|
|
&& (flags & AFF_TYPE_BIN))
|
|
{
|
|
return (0);
|
|
}
|
|
|
|
/* open the file */
|
|
fd = _tcc_open(s1, filename);
|
|
if (fd < 0)
|
|
{
|
|
if (flags & AFF_PRINT_ERROR)
|
|
{
|
|
tcc_error_noabort("file '%s' not found", filename);
|
|
}
|
|
return (FILE_NOT_FOUND);
|
|
}
|
|
|
|
s1->current_filename = filename;
|
|
if (flags & AFF_TYPE_BIN)
|
|
{
|
|
obj_type = tcc_object_type(fd, &fhdr);
|
|
lseek(fd, 0, SEEK_SET);
|
|
|
|
switch (obj_type)
|
|
{
|
|
case TCC_BINTYPE_COFF:
|
|
ret = coff_load_file(s1, fd, 0);
|
|
break;
|
|
|
|
case TCC_BINTYPE_ARCHIVE:
|
|
ret = archive_load(s1, fd, !(flags & AFF_WHOLE_ARCHIVE));
|
|
break;
|
|
|
|
default:
|
|
tcc_error_noabort("%s: unrecognized file type", filename);
|
|
break;
|
|
}
|
|
close(fd);
|
|
}
|
|
else
|
|
{
|
|
/* update target deps */
|
|
dynarray_add(&s1->target_deps, &s1->nb_target_deps, tcc_strdup(filename));
|
|
ret = tcc_compile(s1, flags, filename, fd);
|
|
}
|
|
s1->current_filename = NULL;
|
|
return (ret);
|
|
}
|
|
|
|
static int
|
|
guess_filetype(const char *filename)
|
|
{
|
|
int filetype = 0;
|
|
if (1) {
|
|
/* use a file extension to detect a filetype */
|
|
const char *ext = tcc_fileextension(filename);
|
|
if (ext[0]) {
|
|
ext++;
|
|
if (!strcmp(ext, "S"))
|
|
filetype = AFF_TYPE_ASMPP;
|
|
else if (!strcmp(ext, "s"))
|
|
filetype = AFF_TYPE_ASM;
|
|
else if (!PATHCMP(ext, "c")
|
|
|| !PATHCMP(ext, "h")
|
|
|| !PATHCMP(ext, "i"))
|
|
filetype = AFF_TYPE_C;
|
|
else
|
|
filetype |= AFF_TYPE_BIN;
|
|
} else {
|
|
filetype = AFF_TYPE_C;
|
|
}
|
|
}
|
|
return filetype;
|
|
}
|
|
|
|
int tcc_add_file(TCCState *s, const char *filename)
|
|
{
|
|
return tcc_add_file_internal(s, filename, s->filetype | AFF_PRINT_ERROR);
|
|
}
|
|
|
|
int tcc_add_library_path(TCCState *s, const char *pathname)
|
|
{
|
|
tcc_split_path(s, &s->library_paths, &s->nb_library_paths, pathname);
|
|
return 0;
|
|
}
|
|
|
|
static int tcc_add_library_internal(TCCState *s1, const char *fmt,
|
|
const char *filename, int flags, char **paths, int nb_paths)
|
|
{
|
|
char buf[1024];
|
|
int i, ret;
|
|
|
|
for(i = 0; i < nb_paths; i++) {
|
|
snprintf(buf, sizeof(buf), fmt, paths[i], filename);
|
|
ret = tcc_add_file_internal(s1, buf, flags & ~AFF_PRINT_ERROR);
|
|
if (ret != FILE_NOT_FOUND)
|
|
return ret;
|
|
}
|
|
if (flags & AFF_PRINT_ERROR)
|
|
tcc_error_noabort("library '%s' not found", filename);
|
|
return FILE_NOT_FOUND;
|
|
}
|
|
|
|
/* find and load a dll. Return non zero if not found */
|
|
int tcc_add_dll(TCCState *s, const char *filename, int flags)
|
|
{
|
|
return tcc_add_library_internal(s, "%s/%s", filename, flags,
|
|
s->library_paths, s->nb_library_paths);
|
|
}
|
|
|
|
/* find [cross-]libtcc1.a and tcc helper objects in library path */
|
|
int tcc_add_support(TCCState *s1, const char *filename)
|
|
{
|
|
char buf[100];
|
|
if (CONFIG_TCC_CROSSPREFIX[0])
|
|
filename = strcat(strcpy(buf, CONFIG_TCC_CROSSPREFIX), filename);
|
|
return tcc_add_dll(s1, filename, AFF_PRINT_ERROR);
|
|
}
|
|
|
|
int tcc_add_crt(TCCState *s1, const char *filename)
|
|
{
|
|
return tcc_add_library_internal(s1, "%s/%s",
|
|
filename, AFF_PRINT_ERROR, s1->crt_paths, s1->nb_crt_paths);
|
|
}
|
|
|
|
/* the library name is the same as the argument of the '-l' option */
|
|
int tcc_add_library(TCCState *s, const char *libraryname)
|
|
{
|
|
static const char * const libs[] = { "%s/lib%s.a", NULL };
|
|
const char * const *pp = libs;
|
|
int flags = s->filetype & AFF_WHOLE_ARCHIVE;
|
|
|
|
while (*pp) {
|
|
int ret = tcc_add_library_internal(s, *pp,
|
|
libraryname, flags, s->library_paths, s->nb_library_paths);
|
|
if (ret != FILE_NOT_FOUND)
|
|
return ret;
|
|
++pp;
|
|
}
|
|
return tcc_add_dll(s, libraryname, AFF_PRINT_ERROR);
|
|
}
|
|
|
|
/* handle #pragma comment(lib,) */
|
|
void tcc_add_pragma_libs(TCCState *s1)
|
|
{
|
|
int i;
|
|
for (i = 0; i < s1->nb_pragma_libs; i++)
|
|
tcc_add_library(s1, s1->pragma_libs[i]);
|
|
}
|
|
|
|
void tcc_set_lib_path(TCCState *s, const char *path)
|
|
{
|
|
tcc_free(s->tcc_lib_path);
|
|
s->tcc_lib_path = tcc_strdup(path);
|
|
}
|
|
|
|
/********************************************************/
|
|
/* options parser */
|
|
|
|
#ifdef TCC_MEMORY_DEBUG
|
|
extern size_t mem_max_size;
|
|
#endif /* TCC_MEMORY_DEBUG */
|
|
|
|
PUB_FUNC void tcc_print_stats(TCCState *s1, unsigned total_time)
|
|
{
|
|
if (!total_time)
|
|
total_time = 1;
|
|
fprintf(stderr, "# %d idents, %d lines, %u bytes\n"
|
|
"# %0.3f s, %u lines/s, %0.1f MB/s\n",
|
|
total_idents, total_lines, total_bytes,
|
|
(double)total_time/1000,
|
|
(unsigned)total_lines*1000/total_time,
|
|
(double)total_bytes/1000/total_time);
|
|
fprintf(stderr, "# text %u, data.rw %u, data.ro %u, bss %u bytes\n",
|
|
s1->total_output[0],
|
|
s1->total_output[1],
|
|
s1->total_output[2],
|
|
s1->total_output[3]
|
|
);
|
|
#ifdef TCC_MEMORY_DEBUG
|
|
fprintf(stderr, "# memory usage");
|
|
fprintf(stderr, " %d max (bytes)\n", mem_max_size);
|
|
#endif
|
|
}
|