blob: 17c5ea2d7946a5e942e1d935e8fb88a67a4d5a24 [file] [log] [blame] [raw]
/* cl2cc
Copyright 2015 libdll.so
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.
*/
#include <windows.h>
#include <limits.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <assert.h>
#define VERSION "1.0"
static char *cc_command_line;
static unsigned int cc_command_line_length;
static unsigned int cc_command_line_max_length;
static void __attribute__((__noreturn__)) fatal(int e) {
assert(e);
MessageBoxA(NULL, strerror(e), NULL, MB_ICONHAND);
exit(-e);
}
void init_command_line() {
cc_command_line_max_length = PATH_MAX * 2 + 1;
cc_command_line = malloc(cc_command_line_max_length);
if(!cc_command_line) fatal(ENOMEM);
const char *cc = getenv("CC");
if(!cc) cc = "cc";
size_t len = strlen(cc);
strcpy(cc_command_line, cc);
cc_command_line_length = len;
}
static int have_space(const char *s) {
while(*s) {
if(*s == ' ' || *s == ' ') return 1;
s++;
}
return 0;
}
void add_arg(const char *arg) {
size_t len = strlen(arg) + 1;
//if(have_space(arg)) len += 2;
int need_quote = have_space(arg);
//if(need_quote) len += 2;
if(cc_command_line_length + len + need_quote ? 2 : 0 > cc_command_line_max_length) {
cc_command_line_max_length += PATH_MAX;
cc_command_line = realloc(cc_command_line, cc_command_line_max_length);
if(!cc_command_line) fatal(ENOMEM);
}
cc_command_line[cc_command_line_length] = ' ';
if(need_quote) cc_command_line[++cc_command_line_length] = '\"';
memcpy(cc_command_line + cc_command_line_length + 1, arg, len);
cc_command_line_length += len;
if(need_quote) {
cc_command_line[cc_command_line_length++] = '\"';
cc_command_line[cc_command_line_length] = 0;
}
}
static int get_last_dot(const char *s, size_t len) {
while(--len) {
if(s[len] == '.') break;
if(s[len] == '/') return -1;
}
if(!len) return -1;
return len;
}
static int get_file_name(const char *s, size_t len) {
//int i = 0;
//while(s[i] != '/') if(i == len) return 0;
while(--len) if(s[len] == '/' || s[len] == '\\') break;
return len ? len + 1 : 0;
}
int start_cc() {
const char *compiler = getenv("CC_LOCATION");
STARTUPINFOA si = { .cb = sizeof(STARTUPINFOA) };
PROCESS_INFORMATION pi;
while(!CreateProcessA(compiler, cc_command_line, NULL, NULL, 0, 0, NULL, NULL, &si, &pi)) {
if(compiler) {
compiler = NULL;
continue;
}
fprintf(stderr, "CreateProcessA failed, error %lu\n", GetLastError());
exit(127);
}
unsigned long int r;
WaitForSingleObject(pi.hProcess, INFINITE);
GetExitCodeProcess(pi.hProcess, &r);
//if(r || !target.name) return r;
return r;
}
void define(const char *d) {
char buffer[2 + strlen(d) + 1];
strcpy(buffer, "-D");
strcpy(buffer + 2, d);
add_arg(buffer);
}
void undefine(const char *u) {
char buffer[2 + strlen(u) + 1];
strcpy(buffer, "-U");
strcpy(buffer + 2, u);
add_arg(buffer);
}
static void add_paths(const char *path_set, void (*add)(const char *)) {
char buffer[PATH_MAX + 1], *p = buffer;
do {
do {
*p++ = *path_set++;
if(p - buffer > PATH_MAX) {
p = buffer;
while(*++path_set != ';') if(!*path_set) return;
path_set++;
}
} while(*path_set && *path_set != ';');
*p = 0;
add(buffer);
p = buffer;
if(*path_set) path_set++;
} while(*path_set);
}
void add_include_path(const char *path) {
char buffer[2 + strlen(path) + 1];
strcpy(buffer, "-I");
strcpy(buffer + 2, path);
add_arg(buffer);
}
void add_library_path(const char *path) {
char buffer[2 + strlen(path) + 1];
strcpy(buffer, "-L");
strcpy(buffer + 2, path);
add_arg(buffer);
}
void add_library(const char *lib) {
size_t len = strlen(lib);
if(strncmp(lib + len - 4, ".lib", 4) == 0) len -= 4;
char buffer[2 + len + 1];
strcpy(buffer, "-l");
memcpy(buffer + 2, lib, len);
buffer[len] = 0;
add_arg(buffer);
}
static const char *first_input_file;
void add_input_file(const char *file) {
if(!first_input_file) first_input_file = file;
add_arg(file);
}
void set_output_file(const char *file) {
add_arg("-o");
add_arg(file);
}
static void print_help() {
puts("libdll.so cl2cc " VERSION);
puts("Copyright 2015 libdll.so");
puts("This is free software; published under the GNU GPL, version 2 or later.");
puts("There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A");
puts("PARTICULAR PURPOSE.\n");
puts("Usage: cl2cc [<cl option> ...] <file> [...] [-link <link option> [...]]\n");
puts("All options can be starts with - or /");
}
static struct warning_table {
const char *name;
unsigned int number;
} gcc_to_cl[] = {
{ "implicit-function-declaration", 4013 },
{ "unknown-pragmas", 4068 },
{ "unused-parameter", 4100 },
{ "unused-variable", 4101 }, // 未引用的局部变量
{ "unused-label", 4102 },
{ "unused-but-set-variable", 4189 }, // 局部变量已初始化但不引用
{ "overloaded-virtual", 4264 },
{ "implicit-int", 4431 },
{ "undef", 4668 },
{ "deprecated", 4996 },
{ "deprecated-declarations", 4996 },
{ "uninitialized", 4700 }
};
int disable_warning_by_number(unsigned int number) {
if(number > 4999) return -1;
int i;
for(i = 0; i < sizeof gcc_to_cl / sizeof(struct warning_table); i++) {
struct warning_table *p = gcc_to_cl + i;
if(p->number == number) {
char buffer[5 + strlen(p->name) + 1];
memcpy(buffer, "-Wno-", 4);
strcpy(buffer + 4, p->name);
add_arg(buffer);
return 1;
}
}
return 0;
}
static int get_line_from_file(char *line, const char *file, size_t count) {
fprintf(stderr, "function: get_line_from_file(%p, %p<%s>, %u)\n", line, file, file, (unsigned int)count);
fprintf(stdout, "function: get_line_from_file(%p, %p<%s>, %u)\n", line, file, file, (unsigned int)count);
fflush(stdout);
char buffer[count];
void *f = CreateFileA(file, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if(!f) return -1;
unsigned long int read_size;
if(!ReadFile(f, buffer, count, &read_size, NULL)) return -1;
//buffer[read_size / sizeof(char)] = 0;
//wcstombs(line, buffer + 1, read_size);
memcpy(line, buffer, read_size);
int r = read_size;
char *p = line;
printf("read_size = %lu\n", read_size);
fflush(stdout);
while(p - line < read_size) {
if(*p++ == '\r' && *p == '\n') {
char *np = p;
p[-1] = ' ';
//while((*np = *++np));
while((np[0] = np[1])) np++;
r--;
}
}
return r;
}
//static int get_arg_len(const char *s) {
//}
int expend_command_line_from_file(/*const char *file*/) {
#define BUFFER_SIZE (PATH_MAX * 4)
STARTUPINFOA si = { .cb = sizeof(STARTUPINFOA) };
PROCESS_INFORMATION pi;
unsigned long int r;
char program[PATH_MAX + 1];
if(!GetModuleFileNameA(NULL, program, sizeof program)) {
fprintf(stderr, "GetModuleFileNameA failed, error %lu\n", GetLastError());
return 1;
}
char *old_command_line = GetCommandLineA();
char new_command_line[BUFFER_SIZE], *p = new_command_line;
//if(!get_line_from_file(new_))
do {
if(*old_command_line == '@') {
//int old_len = get_arg_len(old_command_line)
int old_len = 0;
char file[PATH_MAX];
old_command_line++;
while(*old_command_line && *old_command_line != ' ' && *old_command_line != ' ') {
file[old_len++] = *old_command_line++;
}
file[old_len] = 0;
int new_len = get_line_from_file(p, file, sizeof new_command_line - (p - new_command_line) - 1);
if(new_len < 0) {
fprintf(stderr, "failed to read command file %s, %lu\n", file, GetLastError());
return 1;
}
p += new_len;
} else *p++ = *old_command_line++;
} while(*old_command_line);
*p = 0;
fprintf(stdout, "[stdout] old_command_line: \"%s\", program: \"%s\", new_command_line: \"%s\"\n", old_command_line, program, new_command_line);
fprintf(stderr, "[stderr] old_command_line: \"%s\", program: \"%s\", new_command_line: \"%s\"\n", old_command_line, program, new_command_line);
if(!CreateProcessA(program, new_command_line, NULL, NULL, 0, 0, NULL, NULL, &si, &pi)) {
fprintf(stderr, "failed to restart %s, error %lu\n", program, GetLastError());
return 1;
}
WaitForSingleObject(pi.hProcess, INFINITE);
GetExitCodeProcess(pi.hProcess, &r);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
return r;
}
int main(int argc, char **argv) {
#define UNRECOGNIZED_OPTION(O) \
{ \
fprintf(stderr, "%s: warning: ignoring unknown option '%s'\n", \
argv[0], (O)); \
break; \
}
#define CHECK_OPTION_AND_ITS_ARGUMENT(L,O,S) \
{ \
if((O)[L] && (O)[L] != S) UNRECOGNIZED_OPTION(*v); \
if(!(O)[L] || !(O)[(L)+1]) { \
fprintf(stderr, "%s: error: '%s' requires an argument\n", argv[0], *v); \
return 2; \
} \
}
const char *include_path = getenv("INCLUDE");
if(include_path) add_paths(include_path, add_include_path);
const char *library_path = getenv("LIB");
if(library_path) add_paths(library_path, add_library_path);
int no_link = 0;
int linker_option = 0;
//int output_file_seted = 0;
const char *output_file = NULL;
char **v = argv;
init_command_line();
while(*++v) {
if(**v == '@') {
return expend_command_line_from_file(*v + 1);
} else if(**v == '-' || **v == '/') {
const char *arg = *v + 1;
if(linker_option) {
if(strcasecmp(arg, "debug") == 0) {
add_arg("-g");
} else if(strcasecmp(arg, "dll") == 0) {
add_arg("--shared");
} else if(strncasecmp(arg, "entry", 5) == 0) {
if(arg[5] == ':') {
const char *a = arg + 6;
if(!*a) goto no_arg;
size_t alen = strlen(a);
char ld_flag[7 + alen + 1];
strcpy(ld_flag, "-Wl,-e,");
strcpy(ld_flag + 7, a);
add_arg(ld_flag);
} else {
if(!arg[5]) {
no_arg:
fprintf(stderr, "%s: error: no argument specified with option '%s'",
argv[0], *v);
return 122;
}
while(1) UNRECOGNIZED_OPTION(*v);
}
} else if(strncasecmp(arg, "largeaddressaware", 17) == 0) {
if(arg[17] == ':') {
const char *a = arg + 18;
if(strcasecmp(a, "no") == 0) {
add_arg("-Wl,--no-large-address-aware");
} else {
fprintf(stderr, "%s: error: syntax error in option '%s'",
argv[0], *v);
return 93;
}
} else if(arg[17]) while(1) {
UNRECOGNIZED_OPTION(*v);
} else add_arg("-Wl,--large-address-aware");
} else if(strcasecmp(arg, "nologo") == 0) {
// Do nothing
} else if(strncasecmp(arg, "subsystem", 9) == 0) {
__label__ no_arg;
// -Wl,--subsystem,
if(arg[9] == ':') {
const char *a = arg + 10;
if(!*a) goto no_arg;
size_t alen = strlen(a);
char ld_flag[16 + alen + 1];
strcpy(ld_flag, "-Wl,--subsystem,");
strcpy(ld_flag + 16, a);
add_arg(ld_flag);
} else {
if(!arg[9]) {
no_arg:
fprintf(stderr, "%s: error: no argument specified with option '%s'",
argv[0], *v);
return 122;
}
while(1) UNRECOGNIZED_OPTION(*v);
}
} else if(strcasecmp(arg, "wx") == 0) {
add_arg("-Werror");
} else while(1) UNRECOGNIZED_OPTION(*v);
} else switch(*arg) {
case 'c':
if(arg[1]) UNRECOGNIZED_OPTION(*v);
add_arg("-c");
no_link = 1;
break;
case 'D':
if(arg[1]) {
if(**v != '-') **v = '-';
add_arg(*v);
} else {
const char *d = *++v;
if(!d) {
fprintf(stderr, "%s: error: '%s' requires an argument\n", argv[0], *v);
return 2;
}
define(d);
}
break;
case 'E':
if(arg[1] && (arg[1] != 'P' || arg[2])) UNRECOGNIZED_OPTION(*v);
if(arg[1] == 'P') add_arg("-P");
add_arg("-E");
break;
case 'F':
switch(arg[1]) {
case 0:
fprintf(stderr, "%s: error: '%s' requires an argument\n", argv[0], *v);
return 2;
case 'e':
if(no_link) break;
//set_output_file(arg + 2);
//output_file_seted = 1;
output_file = arg + 2;
break;
case 'o':
if(!no_link) {
add_arg("-c");
no_link = 1;
}
//set_output_file(arg + 2);
//output_file_seted = 1;
output_file = arg + 2;
break;
case 'a':
case 'A':
case 'd':
case 'm':
case 'p':
case 'r':
case 'R':
fprintf(stderr, "%s: warning: '%s' is not supported", argv[0], *v);
break;
case 'I':
if(!arg[2]) {
fprintf(stderr, "%s: error: '%s' requires an argument\n", argv[0], *v);
return 2;
}
add_arg("--include");
add_arg(arg + 2);
break;
default:
UNRECOGNIZED_OPTION(*v);
}
break;
case 'G':
switch(arg[1]) {
case 'F':
if(arg[2]) UNRECOGNIZED_OPTION(*v);
add_arg("-fno-writable-strings");
break;
case 'X':
if(arg[2]) {
if(arg[2] == '-') add_arg("-fno-exceptions");
else UNRECOGNIZED_OPTION(*v);
} else add_arg("-fexceptions");
case 'e':
case 'Z':
if(arg[2]) UNRECOGNIZED_OPTION(*v);
add_arg("-fstack-check");
default:
UNRECOGNIZED_OPTION(*v);
}
case 'J':
if(arg[1]) UNRECOGNIZED_OPTION(*v);
add_arg("-funsigned-char");
break;
case 'L':
//if(!arg[1]) UNRECOGNIZED_OPTION(*v);
switch(arg[1]) {
//case 0:
// UNRECOGNIZED_OPTION(*v);
case 'D':
if(arg[2] && arg[2] != 'd') UNRECOGNIZED_OPTION(*v);
add_arg("--shared");
break;
case 'N':
fprintf(stderr, "%s: error: '%s' is not supported\n",
argv[0], *v);
break;
default:
UNRECOGNIZED_OPTION(*v);
}
break;
case 'M':
switch(arg[1]) {
case 'D':
if(arg[2] && arg[2] != 'd') UNRECOGNIZED_OPTION(*v);
break;
case 'T':
if(arg[2] && arg[2] != 'd') UNRECOGNIZED_OPTION(*v);
fprintf(stderr, "%s: warning: linking with %s is not supported\n",
argv[0], arg[2] ? "libcmtd" : "libcmt");
break;
default:
UNRECOGNIZED_OPTION(*v);
}
break;
case 'O':
switch(arg[1]) {
case 0:
fprintf(stderr, "%s: warning: '%s' has been deprecated\n", argv[0], *v);
case '1':
if(arg[2]) UNRECOGNIZED_OPTION(*v);
add_arg("-Os");
break;
case '2':
if(arg[2]) UNRECOGNIZED_OPTION(*v);
add_arg("-Ofast");
break;
case 'b': {
const char *n = arg + 2;
if(!*n) n = "0";
char buffer[15 + strlen(n) + 1];
strcpy(buffer, "-finline-limit=");
strcpy(buffer + 15, n);
add_arg(buffer);
break;
}
case 'd':
if(arg[2]) UNRECOGNIZED_OPTION(*v);
add_arg("-O0");
break;
case 'g':
if(arg[2]) UNRECOGNIZED_OPTION(*v);
add_arg("-O2");
break;
case 'i':
if(arg[2]) {
if(arg[2] == '-') add_arg("-fno-builtin");
else UNRECOGNIZED_OPTION(*v);
}
break;
case 's':
if(arg[2]) UNRECOGNIZED_OPTION(*v);
add_arg("-Os");
break;
case 't':
if(arg[2]) UNRECOGNIZED_OPTION(*v);
add_arg("-Ofast");
break;
case 'x':
if(arg[2]) UNRECOGNIZED_OPTION(*v);
add_arg("-O3");
break;
case 'y':
if(arg[2]) {
if(arg[2] == '-') add_arg("-fno-omit-frame-pointer");
else UNRECOGNIZED_OPTION(*v);
} else add_arg("-fomit-frame-pointer");
break;
default:
UNRECOGNIZED_OPTION(*v);
}
case 'T':
switch(arg[1]) {
case 0:
fprintf(stderr, "%s: error: '%s' requires an argument\n", argv[0], *v);
return 2;
case 'c':
add_arg("-xc");
add_input_file(arg + 2);
add_arg("-xnone");
break;
case 'p':
add_arg("-xc++");
add_input_file(arg + 2);
add_arg("-xnone");
break;
default:
UNRECOGNIZED_OPTION(*v);
}
break;
case 'U':
if(arg[1]) {
if(**v != '-') **v = '-';
add_arg(*v);
} else {
const char *d = *++v;
if(!d) {
fprintf(stderr, "%s: error: '%s' requires an argument\n", argv[0], *v);
return 2;
}
undefine(d);
}
break;
case 'u':
if(arg[1]) UNRECOGNIZED_OPTION(*v);
add_arg("-undef");
break;
#if 0
case 'v': // For debugging
if(arg[1]) UNRECOGNIZED_OPTION(*v);
add_arg("-v");
break;
#endif
case 'W':
switch(arg[1]) {
case 0:
fprintf(stderr, "%s: error: '%s' requires an argument\n", argv[0], *v);
return 2;
case '0':
add_arg("-w");
break;
case '1':
case '2':
break;
case '3':
add_arg("-Wall");
break;
case '4':
add_arg("-Wextra");
break;
case 'L':
break;
case 'X':
add_arg("-Werror");
break;
default:
if(strcmp(arg + 1, "all") == 0) {
add_arg("-Wall");
add_arg("-Wextra");
break;
}
fprintf(stderr, "%s: error: invalid numeric argument '%s'", argv[0], *v);
return 2;
}
break;
case 'w':
switch(arg[1]) {
case 0:
add_arg("-w");
break;
case 'd':
if(!arg[2]) {
fprintf(stderr, "%s: error: '%s' requires an argument", argv[0], *v);
return 2;
}
int n = atoi(arg + 2);
if(n < 0 || disable_warning_by_number(n) < 0) {
fprintf(stderr, "%s: warning: invalid value '%d' for '-wd'; assuming '4999'",
argv[0], n);
return 2;
}
break;
case 'e':
if(!arg[2]) {
fprintf(stderr, "%s: error: '%s' requires an argument", argv[0], *v);
return 2;
}
fprintf(stderr, "%s: warning: '%s': %s\n", argv[0], *v, strerror(ENOSYS));
break;
case 'o':
case '0' ... '9':
fprintf(stderr, "%s: warning: '%s' is not supported\n", argv[0], *v);
break;
default:
UNRECOGNIZED_OPTION(*v);
}
break;
case 'X':
if(arg[1]) UNRECOGNIZED_OPTION(*v);
add_arg("-nostdinc");
break;
case 'Z':
if(!arg[1]) {
fprintf(stderr, "%s: error: '%s' requires an argument\n", argv[0], *v);
return 2;
}
switch(arg[1]) {
case 'i':
if(arg[2]) UNRECOGNIZED_OPTION(*v);
add_arg("-g");
break;
case 'a':
if(arg[2]) UNRECOGNIZED_OPTION(*v);
add_arg("--pedantic");
break;
case 'e':
if(arg[2]) UNRECOGNIZED_OPTION(*v);
break;
case 's':
if(arg[2]) UNRECOGNIZED_OPTION(*v);
add_arg("-fsyntax-only");
break;
default:
UNRECOGNIZED_OPTION(*v);
}
break;
help:
case '?':
print_help();
return 0;
default:
if(strcmp(arg, "help") == 0) goto help;
else if(strncmp(arg, "fp", 2) == 0) {
CHECK_OPTION_AND_ITS_ARGUMENT(2, arg, ':');
const char *m = arg + 3;
if(strcmp(m, "fast") == 0) add_arg("-fexcess-precision=fast");
else if(strcmp(m, "precise") == 0 || strcmp(m, "strict") == 0) {
add_arg("-fexcess-precision=standard");
}
} else if(strncmp(arg, "arch", 4) == 0) {
CHECK_OPTION_AND_ITS_ARGUMENT(4, arg, ':');
const char *a = arg + 5;
if(strcmp(a, "SSE") == 0) add_arg("-msse");
else if(strcmp(a, "SSE2") == 0) add_arg("-msse2");
else {
fprintf(stderr, "%s: error: unrecognized architecture %s\n", argv[0], a);
return 2;
}
} else if(strcmp(arg, "openmp") == 0) {
add_arg("-fopenmp");
} else if(strcmp(arg, "link") == 0) {
linker_option = 1;
} else if(strcmp(arg, "showIncludes") == 0) {
add_arg("-M");
} else if(strcmp(arg, "nologo") == 0) {
// Do nothing
} else {
UNRECOGNIZED_OPTION(*v);
}
break;
}
} else {
(linker_option ? add_library : add_input_file)(*v);
}
}
if(!linker_option) {
if(!first_input_file) {
fprintf(stderr, "%s: error: missing source filename\n", argv[0]);
return 2;
}
if(!output_file) {
size_t len = strlen(first_input_file);
int n = get_last_dot(first_input_file, len);
if(n >= 0) len = n;
char *p = malloc(len + 5);
if(!p) {
perror(argv[0]);
return 1;
}
memcpy(p, first_input_file, len);
strcpy(p + len, no_link ? ".obj" : ".exe");
output_file = p;
//set_output_file(p);
} else {
size_t orig_len = strlen(output_file);
if(output_file[orig_len - 1] == '/') {
size_t add_len = strlen(first_input_file);
int n1 = get_file_name(first_input_file, add_len);
int n2 = get_last_dot(first_input_file, add_len);
if(n2 >= 0) add_len = n2;
if(n1 > 0) {
assert(n1 < add_len);
first_input_file += n1;
add_len -= n1;
}
char *p = malloc(orig_len + add_len + 5);
if(!p) {
perror(argv[0]);
return 1;
}
memcpy(p, output_file, orig_len);
memcpy(p + orig_len, first_input_file, add_len);
strcpy(p + orig_len + add_len, no_link ? ".obj" : ".exe");
output_file = p;
}
}
}
set_output_file(output_file);
puts(cc_command_line);
return start_cc();
//return 0;
}