src/qcc.c
The Basilisk C to C99 pre-processor
This is the front-end for the Basilisk C to C99 translator described in ast/README.
Usage:
qcc [OPTIONS] FILE.c
A summary of the options/switches:
-grid=GRID
: specifies the grid to use (overloads any “in file” includes)-MD
: generates .d dependency file-tags
: generates .tags file-python
: generates python wrapper code-debug
: internal debugging-events
: displays a trace of events on standard error-catch
: catch floating point errors-source
: generates C99 source file (with an underscore prefix)-prepost
: as -source but before expansion of postmacros-autolink
: uses the ‘autolink’ pragma to link required libraries-progress
: the running code will generate a ‘progress’ file-cadna
: support for CADNA-nolineno
: does not generate code containing code line numbers-gpu
: computation is done on GPU by default (this is the default)-cpu
: computation is done on CPU by default-run=INT
: runs the code with the interpreter with the verbosity level given by INT-dimensions[=FILE]
: outputs a summary of the dimensions in a file which is the source file with a.dims
extension if FILE == ‘dims’ or the given FILE name. if FILE is not specified the dimensions are written on stderr.-disable-dimensions
: do not check dimensional consistency-non-finite
: also outputs the dimensions of “non-finite” constants-redundant
: also outputs the dimensions of redundant constants-Wdimensions
: only warns on dimensional errors-maxcalls=VAL
: maximum number of calls for the interpreter. The default is 15 millions. A negative value means no limit.
All other options will be passed directly to the C compiler.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <assert.h>
#include "ast/ast.h"
int dimension = 2, bghosts = 0, layers = 0;
int debug = 0, catch = 0, cadna = 0, nolineno = 0, events = 0, progress = 0;
int parallel = 0, cpu = 0, gpu = 0;
static FILE * dimensions = NULL;
static int run = -1, finite = 1, redundant = 0, warn = 0, maxcalls = 20000000;
char dir[] = ".qccXXXXXX";
char * autolink = NULL;
int autolinks = 0, source = 0;
char * dname (const char * fname);
FILE * dopen (const char * fname, const char * mode);
void includes (int argc, char ** argv,
char ** grid, int * default_grid,
int * dimension, int * bg, int * layers, int * gpu,
const char * dir);
FILE * writepath (char * path, const char * mode)
{
char * s = path;
while ((s = strchr (s, '/'))) {
*s = '\0';
if (access (path, R_OK|W_OK|X_OK) && mkdir (path, 0700))
return NULL;
*s++ = '/';
}
return fopen (path, mode);
}
static void exiting (void)
{
if (!debug && !strncmp (dir, ".qcc", 4)) {
char command[80] = "rm -r -f ";
(command, dir);
strcat if (system (command) < 0)
fprintf (stderr, "qcc: warning: could not cleanup\n");
}
free (autolink);
}
char * dname (const char * fname)
{
char * out = malloc (strlen (dir) + strlen (fname) + 2);
(out, dir); strcat (out, "/"); strcat (out, fname);
strcpy return out;
}
FILE * dopen (const char * fname, const char * mode)
{
char * out = dname (fname);
FILE * fout = fopen (out, mode);
free (out);
return fout;
}
AstRoot * compdir (FILE * fin, FILE * fout, FILE * swigfp,
char * swigname, char * grid)
{
FILE * fout1 = dopen ("_endfor.c", "w");
AstRoot * ast = endfor (fin, fout1, grid, dimension, nolineno, progress, catch,
, cpu, gpu, source == 2,
parallel, swigname);
swigfpfclose (fout1);
= dopen ("_endfor.c", "r");
fout1 extern int postproc (FILE * fin, FILE * fout, char ** autolink, int nolineno);
extern int postlex_destroy (void);
(fout1, fout, &autolink, nolineno);
postproc ();
postlex_destroyfclose (fout1);
fflush (fout);
if (source && autolinks && autolink)
printf ("%s\n", autolink);
return ast;
}
int main (int argc, char ** argv)
{
char * cc = getenv ("CC99"), command[1000], command1[1000] = "";
if (cc == NULL)
(command, CC99);
strcpy
else(command, cc);
strcpy char * file = NULL;
int i, dep = 0, tags = 0, swig = 0;
for (i = 1; i < argc; i++) {
if (!strncmp (argv[i], "-grid=", 6))
;
else if (!strcmp (argv[i], "-MD"))
= 1;
dep else if (!strcmp (argv[i], "-tags"))
= 1;
tags else if (!strcmp (argv[i], "-python"))
= 1;
swig else if (!strcmp (argv[i], "-debug"))
= 1;
debug else if (!strcmp (argv[i], "-events"))
events = 1;
else if (!strcmp (argv[i], "-catch"))
= 1;
catch else if (!strcmp (argv[i], "-source"))
= 1;
source else if (!strcmp (argv[i], "-prepost"))
= 2;
source else if (!strcmp (argv[i], "-autolink"))
= 1;
autolinks else if (!strcmp (argv[i], "-progress"))
= 1;
progress else if (!strncmp (argv[i], "-run=", 5))
= atoi (argv[i] + 5);
run else if (!strcmp (argv[i], "-disable-dimensions"))
= stdout;
dimensions else if (!strncmp (argv[i], "-dimensions", 11)) {
if (dimensions != stdout) { // dimensions have been disabled
if (*(argv[i] + 11) == '=') {
if (!strcmp (argv[i] + 12, "dims"))
= stdin;
dimensions else {
= fopen (argv[i] + 12, "w");
dimensions if (!dimensions) {
(argv[i] + 12);
perror exit (1);
}
}
}
else= stderr;
dimensions }
}
else if (!strcmp (argv[i], "-non-finite"))
= 0;
finite else if (!strcmp (argv[i], "-redundant"))
= 1;
redundant else if (!strcmp (argv[i], "-Wdimensions"))
= 1;
warn else if (!strncmp (argv[i], "-maxcalls", 9)) {
if (*(argv[i] + 9) == '=')
= atoi (argv[i] + 10);
maxcalls }
else if (!strcmp (argv[i], "-Wall")) {
char * s = strchr (command, ' ');
if (s) {
char command1[1000];
(command1, s);
strcpy *(s+1) = '\0';
(command, argv[i]);
strcat (command, command1);
strcat }
else(command, argv[i]);
strcat }
else if (!strcmp (argv[i], "-cadna")) {
= 1;
cadna char * cc = getenv ("CADNACC");
if (cc == NULL)
(command, CADNACC);
strcpy
else(command, cc);
strcpy }
else if (!strncmp (argv[i], "-Ddimension=", 12))
= 1 + argv[i][12] - '1';
dimension else if (catch && !strncmp (argv[i], "-O", 2))
;
else if (!strcmp (argv[i], "-nolineno"))
= 1;
nolineno else if (!strcmp (argv[i], "-cpu"))
= 1;
cpu else if (!strcmp (argv[i], "-gpu"))
= 0;
cpu else if (!strcmp (argv[i], "-o")) {
(command1, " ");
strcat (command1, argv[i++]);
strcat if (i < argc) {
(command1, " ");
strcat (command1, argv[i]);
strcat }
}
else if (argv[i][0] != '-' &&
(tags || !strcmp (argv[i] + strlen(argv[i]) - 2, ".c"))) {
if (file) {
fprintf (stderr, "usage: qcc -grid=[GRID] [OPTIONS] FILE.c\n");
return 1;
}
= argv[i];
file if (dimensions == stdin) {
int len = strlen (file);
char name[len + 4];
(name, file);
strcpy (name + len - 2, ".dims");
strcpy = fopen (name, "w");
dimensions if (!dimensions) {
(name);
perror exit (1);
}
}
}
else if (!file) {
(command, " ");
strcat (command, argv[i]);
strcat }
else {
(command1, " ");
strcat (command1, argv[i]);
strcat }
}
if (source && !file) {
fprintf (stderr, "usage: qcc -grid=[GRID] [OPTIONS] FILE.c\n");
return 1;
}
if (dimensions == stdin) {
fprintf (stderr, "qcc: error: -dimensions must be given "
"before the .c source file name\n");
exit (1);
}
if (strstr (command, "-D_MPI"))
= 1;
parallel char * openmp = strstr (command, "-fopenmp");
if (openmp) {
= 1;
parallel if (strstr (command, "-D_MPI")) {
fprintf (stderr,
"qcc: warning: OpenMP cannot be used with MPI (yet): "
"switching it off\n");
int i;
for (i = 0; i < strlen("-fopenmp"); i++)
[i] = ' ';
openmp}
else if (swig) {
fprintf (stderr,
"qcc: warning: OpenMP cannot be used with Python (yet): "
"switching it off\n");
int i;
for (i = 0; i < strlen("-fopenmp"); i++)
[i] = ' ';
openmp}
}
int status;
if (debug) {
= system ("rm -r -f .qcc");
status (dir, ".qcc");
strcpy = mkdir (dir, 0700);
status }
else= (mkdtemp (dir) == NULL);
status if (status) {
(dir);
perror return 1;
}
(exiting);
atexit void * ast = NULL;
if (file) {
char * grid = NULL;
int default_grid;
(argc, argv, &grid, &default_grid,
includes &dimension, &bghosts, &layers, &gpu,
|| tags ? NULL : dir);
dep if (gpu)
= 1;
parallel FILE * swigfp = NULL;
char swigname[80] = "";
if (swig) {
(swigname, file);
strcpy char * dot = strchr (swigname, '.');
*dot = '\0'; strcat (swigname, ".i");
= fopen (swigname, "a");
swigfp if (!swigfp) {
fprintf (stderr, "qcc: could not open '%s': ", swigname);
return 1;
}
*dot = '\0';
}
if (!dep && !tags) {
char * basename = strdup (file), * ext = basename;
while (*ext != '\0' && *ext != '.') ext++;
char * cpp = malloc (strlen(basename) + strlen("-cpp") + strlen(ext) + 1);
if (*ext == '.') {
*ext = '\0';
(cpp, basename);
strcpy (cpp, "-cpp");
strcat *ext = '.';
(cpp, ext);
strcat }
else {
(cpp, basename);
strcpy (cpp, "-cpp");
strcat }
free (basename);
FILE * fin = dopen (file, "r");
if (!fin) {
(file);
perror exit (1);
}
FILE * fp = dopen ("_attributes.h", "w");
("typedef struct {\n", fp);
fputs fclose (fp);
= dopen ("_maps.h", "w");
fp fclose (fp);
FILE * fout = dopen (cpp, "w");
if (swig)
("@include <Python.h>\n", fout);
fputs if (gpu)
("@define _GPU 1\n", fout);
fputs ("@if _XOPEN_SOURCE < 700\n"
fputs " @undef _XOPEN_SOURCE\n"
" @define _XOPEN_SOURCE 700\n"
"@endif\n"
"@if _GNU_SOURCE\n"
"@include <stdint.h>\n"
"@include <string.h>\n"
"@include <fenv.h>\n"
"@endif\n",
);
foutif (catch)
("#define TRASH 1\n"
fputs "#define _CATCH last_point = point;\n",
);
fout
else("#define _CATCH\n", fout);
fputs fprintf (fout, "#define dimension %d\n", dimension);
if (bghosts)
fprintf (fout, "#define BGHOSTS %d\n", bghosts);
if (layers)
fprintf (fout, "#define LAYERS 1\n");
("#include \"common.h\"\n", fout);
fputs /* catch */
if (catch)
("void catch_fpe (void);\n", fout);
fputs /* grid */
if (default_grid)
fprintf (fout, "#include \"grid/%s.h\"\n", grid);
char s[81];
while (fgets (s, 81, fin)) {
if (default_grid && strstr (s, "#include \"grid/"))
("\n", fout);
fputs
else(s, fout);
fputs }
if (swigfp)
("#include \"python.h\"\n", fout);
fputs if (progress)
("#include \"grid/progress.h\"\n", fout);
fputs fclose (fout);
fclose (fin);
= dopen (file, "w");
fout if (!fout) {
(file);
perror exit (1);
}
char preproc[1000], * cppcommand = getenv ("CPP99");
(preproc, "cd ");
strcpy (preproc, dir);
strcat (preproc, " && ");
strcat if (!cppcommand && strcmp (CPP99, ""))
= CPP99;
cppcommand if (!cppcommand) {
if (source) {
/* remove -D_GNU_SOURCE flags from $CC99 */
char tmp[1000]; strcpy (tmp, command);
char * s = strtok (tmp, " \t");
while (s) {
if (strncmp (s, "-D_GNU_SOURCE", 13)) {
(preproc, s);
strcat (preproc, " ");
strcat }
= strtok (NULL, " \t");
s }
}
else(preproc, command);
strcat (preproc, " -E");
strcat }
else(preproc, cppcommand);
strcat // remove "-pedantic option from preprocessor
// This is mostly to avoid the preprocessor warning:
// "ISO C99 requires at least one argument in a variadic macro"
// note that the option is kept for the final compilation
char * pedantic = strstr (preproc, "-pedantic");
if (pedantic)
for (i = 0; i < strlen ("-pedantic"); i++)
[i] = ' ';
pedantic(preproc, " -I");
strcat (preproc, BASILISK);
strcat (preproc, "/ast/std");
strcat (preproc, " -I. -I");
strcat (preproc, LIBDIR);
strcat (preproc, " ");
strcat if (events) {
(preproc, " -DDEBUG_EVENTS=1 -DBASILISK=\"\\\"");
strcat (preproc, BASILISK);
strcat (preproc, "\\\"\" ");
strcat }
if (nolineno)
(preproc, " -D'LINENO=0' ");
strcat (preproc, cpp);
strcat free (cpp);
if (debug) {
fprintf (stderr, "preproc: %s\n", preproc);
(preproc, " | tee _preproc.c");
strcat }
= popen (preproc, "r");
fin if (!fin) {
fclose (fout);
(preproc);
perror exit (1);
}
= compdir (fin, fout, swigfp, swigname, grid);
ast int status = pclose (fin);
fclose (fout);
if (status == -1 ||
(WIFSIGNALED (status) && (WTERMSIG (status) == SIGINT ||
(status) == SIGQUIT)))
WTERMSIG exit (1);
= dopen ("_tmp", "w");
fout = dopen (file, "r");
fin char line[1024];
// rest of the file
while (fgets (line, 1024, fin)) {
if (!strncmp (line, "#line 0 ", 8))
[6] = '1';
line(line, fout);
fputs }
fclose (fin);
fclose (fout);
char src[80], dst[80];
(src, dir); strcat (src, "/_tmp");
strcpy if (source) {
(dst, "_");
strcpy }
else {
(dst, dir); strcat (dst, "/");
strcpy }
(dst, file);
strcat (src, dst);
rename
(command, " -I");
strcat (command, LIBDIR);
strcat (command, " ");
strcat (command, dir);
strcat (command, "/");
strcat (command, file);
strcat (command, command1);
strcat }
}
else if (dep || tags) {
fprintf (stderr, "usage: qcc -grid=[GRID] [OPTIONS] FILE.c\n");
exit (1);
}
else(command, command1);
strcat /* compilation */
= 0;
status if (!dep && !tags && !source) {
if (autolinks && autolink)
(command, autolink);
strcat if (debug)
fprintf (stderr, "command: %s\n", command);
= system (command);
status if (status == -1 ||
(WIFSIGNALED (status) && (WTERMSIG (status) == SIGINT ||
(status) == SIGQUIT)))
WTERMSIG exit (1);
= WEXITSTATUS (status);
status }
int check_dimensions (void * root,
int nolineno,
int run, FILE * dimensions, int finite, int redundant,
int warn,
int maxcalls);
if (ast &&
== 0 &&
status !check_dimensions (ast, nolineno,
, dimensions, finite, redundant, warn, maxcalls) &&
run!warn)
= 2; // dimensional error
status exit (status);
return status;
}