| 1 | /* |
| 2 | * TCC - Tiny C Compiler |
| 3 | * |
| 4 | * Copyright (c) 2001-2004 Fabrice Bellard |
| 5 | * |
| 6 | * This library is free software; you can redistribute it and/or |
| 7 | * modify it under the terms of the GNU Lesser General Public |
| 8 | * License as published by the Free Software Foundation; either |
| 9 | * version 2 of the License, or (at your option) any later version. |
| 10 | * |
| 11 | * This library is distributed in the hope that it will be useful, |
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 14 | * Lesser General Public License for more details. |
| 15 | * |
| 16 | * You should have received a copy of the GNU Lesser General Public |
| 17 | * License along with this library; if not, write to the Free Software |
| 18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| 19 | */ |
| 20 | |
| 21 | #include "tcc.h" |
| 22 | #if ONE_SOURCE |
| 23 | # include "libtcc.c" |
| 24 | #endif |
| 25 | #include "tcctools.c" |
| 26 | |
| 27 | static const char help[] = |
| 28 | "Tiny C Compiler " TCC_VERSION" - Copyright (C) 2001-2006 Fabrice Bellard\n" |
| 29 | "Usage: tcc [options...] [-o outfile] [-c] infile(s)...\n" |
| 30 | " tcc [options...] -run infile [arguments...]\n" |
| 31 | "General options:\n" |
| 32 | " -c compile only - generate an object file\n" |
| 33 | " -o outfile set output filename\n" |
| 34 | " -run run compiled source\n" |
| 35 | " -fflag set or reset (with 'no-' prefix) 'flag' (see tcc -hh)\n" |
| 36 | " -std=c99 Conform to the ISO 1999 C standard (default).\n" |
| 37 | " -std=c11 Conform to the ISO 2011 C standard.\n" |
| 38 | " -Wwarning set or reset (with 'no-' prefix) 'warning' (see tcc -hh)\n" |
| 39 | " -w disable all warnings\n" |
| 40 | " --version -v show version\n" |
| 41 | " -vv show search paths or loaded files\n" |
| 42 | " -h -hh show this, show more help\n" |
| 43 | " -bench show compilation statistics\n" |
| 44 | " - use stdin pipe as infile\n" |
| 45 | " @listfile read arguments from listfile\n" |
| 46 | "Preprocessor options:\n" |
| 47 | " -Idir add include path 'dir'\n" |
| 48 | " -Dsym[=val] define 'sym' with value 'val'\n" |
| 49 | " -Usym undefine 'sym'\n" |
| 50 | " -E preprocess only\n" |
| 51 | " -C keep comments (not yet implemented)\n" |
| 52 | "Linker options:\n" |
| 53 | " -Ldir add library path 'dir'\n" |
| 54 | " -llib link with dynamic or static library 'lib'\n" |
| 55 | " -r generate (relocatable) object file\n" |
| 56 | " -shared generate a shared library/dll\n" |
| 57 | " -rdynamic export all global symbols to dynamic linker\n" |
| 58 | " -soname set name for shared library to be used at runtime\n" |
| 59 | " -Wl,-opt[=val] set linker option (see tcc -hh)\n" |
| 60 | "Debugger options:\n" |
| 61 | " -g generate runtime debug info\n" |
| 62 | #ifdef CONFIG_TCC_BCHECK |
| 63 | " -b compile with built-in memory and bounds checker (implies -g)\n" |
| 64 | #endif |
| 65 | #ifdef CONFIG_TCC_BACKTRACE |
| 66 | " -bt[N] link with backtrace (stack dump) support [show max N callers]\n" |
| 67 | #endif |
| 68 | "Misc. options:\n" |
| 69 | " -x[c|a|b|n] specify type of the next infile (C,ASM,BIN,NONE)\n" |
| 70 | " -nostdinc do not use standard system include paths\n" |
| 71 | " -nostdlib do not link with standard crt and libraries\n" |
| 72 | " -Bdir set tcc's private include/library dir\n" |
| 73 | " -MD generate dependency file for make\n" |
| 74 | " -MF file specify dependency file name\n" |
| 75 | #if defined(TCC_TARGET_I386) || defined(TCC_TARGET_X86_64) |
| 76 | " -m32/64 defer to i386/x86_64 cross compiler\n" |
| 77 | #endif |
| 78 | "Tools:\n" |
| 79 | " create library : tcc -ar [rcsv] lib.a files\n" |
| 80 | #ifdef TCC_TARGET_PE |
| 81 | " create def file : tcc -impdef lib.dll [-v] [-o lib.def]\n" |
| 82 | #endif |
| 83 | ; |
| 84 | |
| 85 | static const char help2[] = |
| 86 | "Tiny C Compiler " TCC_VERSION" - More Options\n" |
| 87 | "Special options:\n" |
| 88 | " -P -P1 with -E: no/alternative #line output\n" |
| 89 | " -dD -dM with -E: output #define directives\n" |
| 90 | " -pthread same as -D_REENTRANT and -lpthread\n" |
| 91 | " -On same as -D__OPTIMIZE__ for n > 0\n" |
| 92 | " -Wp,-opt same as -opt\n" |
| 93 | " -include file include 'file' above each input file\n" |
| 94 | " -isystem dir add 'dir' to system include path\n" |
| 95 | " -static link to static libraries (not recommended)\n" |
| 96 | " -dumpversion print version\n" |
| 97 | " -print-search-dirs print search paths\n" |
| 98 | " -dt with -run/-E: auto-define 'test_...' macros\n" |
| 99 | "Ignored options:\n" |
| 100 | " --param -pedantic -pipe -s -traditional\n" |
| 101 | "-W... warnings:\n" |
| 102 | " all turn on some (*) warnings\n" |
| 103 | " error stop after first warning\n" |
| 104 | " unsupported warn about ignored options, pragmas, etc.\n" |
| 105 | " write-strings strings are const\n" |
| 106 | " implicit-function-declaration warn for missing prototype (*)\n" |
| 107 | "-f[no-]... flags:\n" |
| 108 | " unsigned-char default char is unsigned\n" |
| 109 | " signed-char default char is signed\n" |
| 110 | " common use common section instead of bss\n" |
| 111 | " leading-underscore decorate extern symbols\n" |
| 112 | " ms-extensions allow anonymous struct in struct\n" |
| 113 | " dollars-in-identifiers allow '$' in C symbols\n" |
| 114 | "-m... target specific options:\n" |
| 115 | " ms-bitfields use MSVC bitfield layout\n" |
| 116 | #ifdef TCC_TARGET_ARM |
| 117 | " float-abi hard/softfp on arm\n" |
| 118 | #endif |
| 119 | #ifdef TCC_TARGET_X86_64 |
| 120 | " no-sse disable floats on x86_64\n" |
| 121 | #endif |
| 122 | "-Wl,... linker options:\n" |
| 123 | " -nostdlib do not link with standard crt/libs\n" |
| 124 | " -[no-]whole-archive load lib(s) fully/only as needed\n" |
| 125 | " -export-all-symbols same as -rdynamic\n" |
| 126 | " -export-dynamic same as -rdynamic\n" |
| 127 | " -image-base= -Ttext= set base address of executable\n" |
| 128 | " -section-alignment= set section alignment in executable\n" |
| 129 | #ifdef TCC_TARGET_PE |
| 130 | " -file-alignment= set PE file alignment\n" |
| 131 | " -stack= set PE stack reserve\n" |
| 132 | " -large-address-aware set related PE option\n" |
| 133 | " -subsystem=[console/windows] set PE subsystem\n" |
| 134 | " -oformat=[pe-* binary] set executable output format\n" |
| 135 | "Predefined macros:\n" |
| 136 | " tcc -E -dM - < nul\n" |
| 137 | #else |
| 138 | " -rpath= set dynamic library search path\n" |
| 139 | " -enable-new-dtags set DT_RUNPATH instead of DT_RPATH\n" |
| 140 | " -soname= set DT_SONAME elf tag\n" |
| 141 | " -Bsymbolic set DT_SYMBOLIC elf tag\n" |
| 142 | " -oformat=[elf32/64-* binary] set executable output format\n" |
| 143 | " -init= -fini= -as-needed -O (ignored)\n" |
| 144 | "Predefined macros:\n" |
| 145 | " tcc -E -dM - < /dev/null\n" |
| 146 | #endif |
| 147 | "See also the manual for more details.\n" |
| 148 | ; |
| 149 | |
| 150 | static const char version[] = |
| 151 | "tcc version " TCC_VERSION" (" |
| 152 | #ifdef TCC_TARGET_I386 |
| 153 | "i386" |
| 154 | #elif defined TCC_TARGET_X86_64 |
| 155 | "x86_64" |
| 156 | #elif defined TCC_TARGET_C67 |
| 157 | "C67" |
| 158 | #elif defined TCC_TARGET_ARM |
| 159 | "ARM" |
| 160 | #elif defined TCC_TARGET_ARM64 |
| 161 | "AArch64" |
| 162 | #elif defined TCC_TARGET_RISCV64 |
| 163 | "riscv64" |
| 164 | #endif |
| 165 | #ifdef TCC_ARM_HARDFLOAT |
| 166 | " Hard Float" |
| 167 | #endif |
| 168 | #ifdef TCC_TARGET_PE |
| 169 | " Windows" |
| 170 | #elif defined(TCC_TARGET_MACHO) |
| 171 | " Darwin" |
| 172 | #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) |
| 173 | " FreeBSD" |
| 174 | #else |
| 175 | " Linux" |
| 176 | #endif |
| 177 | ")\n" |
| 178 | ; |
| 179 | |
| 180 | static void print_dirs(const char *msg, char **paths, int nb_paths) |
| 181 | { |
| 182 | int i; |
| 183 | printf("%s:\n%s" , msg, nb_paths ? "" : " -\n" ); |
| 184 | for(i = 0; i < nb_paths; i++) |
| 185 | printf(" %s\n" , paths[i]); |
| 186 | } |
| 187 | |
| 188 | static void print_search_dirs(TCCState *s) |
| 189 | { |
| 190 | printf("install: %s\n" , s->tcc_lib_path); |
| 191 | /* print_dirs("programs", NULL, 0); */ |
| 192 | print_dirs("include" , s->sysinclude_paths, s->nb_sysinclude_paths); |
| 193 | print_dirs("libraries" , s->library_paths, s->nb_library_paths); |
| 194 | #ifdef TCC_TARGET_PE |
| 195 | printf("libtcc1:\n %s/lib/" TCC_LIBTCC1"\n" , s->tcc_lib_path); |
| 196 | #else |
| 197 | printf("libtcc1:\n %s/" TCC_LIBTCC1"\n" , s->tcc_lib_path); |
| 198 | print_dirs("crt" , s->crt_paths, s->nb_crt_paths); |
| 199 | printf("elfinterp:\n %s\n" , DEFAULT_ELFINTERP(s)); |
| 200 | #endif |
| 201 | } |
| 202 | |
| 203 | static void set_environment(TCCState *s) |
| 204 | { |
| 205 | char * path; |
| 206 | |
| 207 | path = getenv("C_INCLUDE_PATH" ); |
| 208 | if(path != NULL) { |
| 209 | tcc_add_sysinclude_path(s, path); |
| 210 | } |
| 211 | path = getenv("CPATH" ); |
| 212 | if(path != NULL) { |
| 213 | tcc_add_include_path(s, path); |
| 214 | } |
| 215 | path = getenv("LIBRARY_PATH" ); |
| 216 | if(path != NULL) { |
| 217 | tcc_add_library_path(s, path); |
| 218 | } |
| 219 | } |
| 220 | |
| 221 | static char *default_outputfile(TCCState *s, const char *first_file) |
| 222 | { |
| 223 | char buf[1024]; |
| 224 | char *ext; |
| 225 | const char *name = "a" ; |
| 226 | |
| 227 | if (first_file && strcmp(first_file, "-" )) |
| 228 | name = tcc_basename(first_file); |
| 229 | snprintf(buf, sizeof(buf), "%s" , name); |
| 230 | ext = tcc_fileextension(buf); |
| 231 | #ifdef TCC_TARGET_PE |
| 232 | if (s->output_type == TCC_OUTPUT_DLL) |
| 233 | strcpy(ext, ".dll" ); |
| 234 | else |
| 235 | if (s->output_type == TCC_OUTPUT_EXE) |
| 236 | strcpy(ext, ".exe" ); |
| 237 | else |
| 238 | #endif |
| 239 | if (s->output_type == TCC_OUTPUT_OBJ && !s->option_r && *ext) |
| 240 | strcpy(ext, ".o" ); |
| 241 | else |
| 242 | strcpy(buf, "a.out" ); |
| 243 | return tcc_strdup(buf); |
| 244 | } |
| 245 | |
| 246 | static unsigned getclock_ms(void) |
| 247 | { |
| 248 | #ifdef _WIN32 |
| 249 | return GetTickCount(); |
| 250 | #else |
| 251 | struct timeval tv; |
| 252 | gettimeofday(&tv, NULL); |
| 253 | return tv.tv_sec*1000 + (tv.tv_usec+500)/1000; |
| 254 | #endif |
| 255 | } |
| 256 | |
| 257 | int main(int argc0, char **argv0) |
| 258 | { |
| 259 | TCCState *s, *s1; |
| 260 | int ret, opt, n = 0, t = 0, done; |
| 261 | unsigned start_time = 0; |
| 262 | const char *first_file; |
| 263 | int argc; char **argv; |
| 264 | FILE *ppfp = stdout; |
| 265 | |
| 266 | redo: |
| 267 | argc = argc0, argv = argv0; |
| 268 | s = s1 = tcc_new(); |
| 269 | opt = tcc_parse_args(s, &argc, &argv, 1); |
| 270 | |
| 271 | if (n == 0) { |
| 272 | if (opt == OPT_HELP) { |
| 273 | fputs(help, stdout); |
| 274 | if (!s->verbose) |
| 275 | return 0; |
| 276 | ++opt; |
| 277 | } |
| 278 | if (opt == OPT_HELP2) { |
| 279 | fputs(help2, stdout); |
| 280 | return 0; |
| 281 | } |
| 282 | if (opt == OPT_M32 || opt == OPT_M64) |
| 283 | tcc_tool_cross(s, argv, opt); /* never returns */ |
| 284 | if (s->verbose) |
| 285 | printf(version); |
| 286 | if (opt == OPT_AR) |
| 287 | return tcc_tool_ar(s, argc, argv); |
| 288 | #ifdef TCC_TARGET_PE |
| 289 | if (opt == OPT_IMPDEF) |
| 290 | return tcc_tool_impdef(s, argc, argv); |
| 291 | #endif |
| 292 | if (opt == OPT_V) |
| 293 | return 0; |
| 294 | if (opt == OPT_PRINT_DIRS) { |
| 295 | /* initialize search dirs */ |
| 296 | set_environment(s); |
| 297 | tcc_set_output_type(s, TCC_OUTPUT_MEMORY); |
| 298 | print_search_dirs(s); |
| 299 | return 0; |
| 300 | } |
| 301 | |
| 302 | if (s->nb_files == 0) |
| 303 | tcc_error("no input files\n" ); |
| 304 | |
| 305 | if (s->output_type == TCC_OUTPUT_PREPROCESS) { |
| 306 | if (s->outfile && 0!=strcmp("-" ,s->outfile)) { |
| 307 | ppfp = fopen(s->outfile, "w" ); |
| 308 | if (!ppfp) |
| 309 | tcc_error("could not write '%s'" , s->outfile); |
| 310 | } |
| 311 | } else if (s->output_type == TCC_OUTPUT_OBJ && !s->option_r) { |
| 312 | if (s->nb_libraries) |
| 313 | tcc_error("cannot specify libraries with -c" ); |
| 314 | if (s->nb_files > 1 && s->outfile) |
| 315 | tcc_error("cannot specify output file with -c many files" ); |
| 316 | } |
| 317 | |
| 318 | if (s->do_bench) |
| 319 | start_time = getclock_ms(); |
| 320 | } |
| 321 | |
| 322 | set_environment(s); |
| 323 | if (s->output_type == 0) |
| 324 | s->output_type = TCC_OUTPUT_EXE; |
| 325 | tcc_set_output_type(s, s->output_type); |
| 326 | s->ppfp = ppfp; |
| 327 | |
| 328 | if ((s->output_type == TCC_OUTPUT_MEMORY |
| 329 | || s->output_type == TCC_OUTPUT_PREPROCESS) |
| 330 | && (s->dflag & 16)) { /* -dt option */ |
| 331 | if (t) |
| 332 | s->dflag |= 32; |
| 333 | s->run_test = ++t; |
| 334 | if (n) |
| 335 | --n; |
| 336 | } |
| 337 | |
| 338 | /* compile or add each files or library */ |
| 339 | first_file = NULL, ret = 0; |
| 340 | do { |
| 341 | struct filespec *f = s->files[n]; |
| 342 | s->filetype = f->type; |
| 343 | if (f->type & AFF_TYPE_LIB) { |
| 344 | if (tcc_add_library_err(s, f->name) < 0) |
| 345 | ret = 1; |
| 346 | } else { |
| 347 | if (1 == s->verbose) |
| 348 | printf("-> %s\n" , f->name); |
| 349 | if (!first_file) |
| 350 | first_file = f->name; |
| 351 | if (tcc_add_file(s, f->name) < 0) |
| 352 | ret = 1; |
| 353 | } |
| 354 | done = ret || ++n >= s->nb_files; |
| 355 | } while (!done && (s->output_type != TCC_OUTPUT_OBJ || s->option_r)); |
| 356 | |
| 357 | if (s->run_test) { |
| 358 | t = 0; |
| 359 | } else if (s->output_type == TCC_OUTPUT_PREPROCESS) { |
| 360 | ; |
| 361 | } else if (0 == ret) { |
| 362 | if (s->output_type == TCC_OUTPUT_MEMORY) { |
| 363 | #ifdef TCC_IS_NATIVE |
| 364 | ret = tcc_run(s, argc, argv); |
| 365 | #endif |
| 366 | } else { |
| 367 | if (!s->outfile) |
| 368 | s->outfile = default_outputfile(s, first_file); |
| 369 | if (tcc_output_file(s, s->outfile)) |
| 370 | ret = 1; |
| 371 | else if (s->gen_deps) |
| 372 | gen_makedeps(s, s->outfile, s->deps_outfile); |
| 373 | } |
| 374 | } |
| 375 | |
| 376 | if (s->do_bench && done && !(t | ret)) |
| 377 | tcc_print_stats(s, getclock_ms() - start_time); |
| 378 | tcc_delete(s); |
| 379 | if (!done) |
| 380 | goto redo; /* compile more files with -c */ |
| 381 | if (t) |
| 382 | goto redo; /* run more tests with -dt -run */ |
| 383 | if (ppfp && ppfp != stdout) |
| 384 | fclose(ppfp); |
| 385 | return ret; |
| 386 | } |
| 387 | |