2015-08-06 07:22:21 +08:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2015 Andrew Kelley
|
|
|
|
*
|
|
|
|
* This file is part of zig, which is MIT licensed.
|
|
|
|
* See http://opensource.org/licenses/MIT
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "config.h"
|
2015-08-06 11:26:58 +08:00
|
|
|
#include "buffer.hpp"
|
2015-11-24 13:47:25 +08:00
|
|
|
#include "codegen.hpp"
|
2015-12-01 10:58:53 +08:00
|
|
|
#include "os.hpp"
|
2015-12-03 15:47:35 +08:00
|
|
|
#include "error.hpp"
|
2016-01-19 12:28:54 +08:00
|
|
|
#include "parseh.hpp"
|
2015-08-06 11:26:58 +08:00
|
|
|
|
2015-08-06 07:22:21 +08:00
|
|
|
#include <stdio.h>
|
|
|
|
|
2015-11-05 15:05:25 +08:00
|
|
|
static int usage(const char *arg0) {
|
2015-12-04 06:59:14 +08:00
|
|
|
fprintf(stderr, "Usage: %s [command] [options]\n"
|
2015-11-05 15:05:25 +08:00
|
|
|
"Commands:\n"
|
2015-11-28 12:24:11 +08:00
|
|
|
" build create executable, object, or library from target\n"
|
|
|
|
" version print version number and exit\n"
|
2016-01-19 12:28:54 +08:00
|
|
|
" parseh convert a c header file to zig extern declarations\n"
|
2015-12-04 06:59:14 +08:00
|
|
|
"Command: build target\n"
|
2015-11-28 12:24:11 +08:00
|
|
|
" --release build with optimizations on and debug protection off\n"
|
|
|
|
" --static output will be statically linked\n"
|
|
|
|
" --strip exclude debug symbols\n"
|
|
|
|
" --export [exe|lib|obj] override output type\n"
|
|
|
|
" --name [name] override output name\n"
|
|
|
|
" --output [file] override destination path\n"
|
2015-12-01 10:58:53 +08:00
|
|
|
" --verbose turn on compiler debug output\n"
|
2015-12-01 16:06:10 +08:00
|
|
|
" --color [auto|off|on] enable or disable colored error messages\n"
|
2015-12-16 03:44:42 +08:00
|
|
|
" --libc-path [path] set the C compiler data path\n"
|
2016-01-19 12:28:54 +08:00
|
|
|
"Command: parseh target\n"
|
|
|
|
" -isystem [dir] add additional search path for other .h files\n"
|
|
|
|
" -dirafter [dir] same as -isystem but do it last\n"
|
|
|
|
" -B[prefix] set the C compiler data path\n"
|
2015-08-06 07:22:21 +08:00
|
|
|
, arg0);
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
|
|
|
|
2015-12-04 06:59:14 +08:00
|
|
|
static int version(const char *arg0, int argc, char **argv) {
|
2015-11-28 12:24:11 +08:00
|
|
|
printf("%s\n", ZIG_VERSION_STRING);
|
|
|
|
return EXIT_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2015-12-01 10:58:53 +08:00
|
|
|
struct Build {
|
|
|
|
const char *in_file;
|
|
|
|
const char *out_file;
|
|
|
|
bool release;
|
|
|
|
bool strip;
|
|
|
|
bool is_static;
|
|
|
|
OutType out_type;
|
|
|
|
const char *out_name;
|
|
|
|
bool verbose;
|
2015-12-01 16:06:10 +08:00
|
|
|
ErrColor color;
|
2015-12-16 03:44:42 +08:00
|
|
|
const char *libc_path;
|
2015-12-01 10:58:53 +08:00
|
|
|
};
|
2015-08-06 07:22:21 +08:00
|
|
|
|
2015-12-04 06:59:14 +08:00
|
|
|
static int build(const char *arg0, int argc, char **argv) {
|
2015-12-03 15:47:35 +08:00
|
|
|
int err;
|
2015-12-01 10:58:53 +08:00
|
|
|
Build b = {0};
|
|
|
|
|
2015-12-04 06:59:14 +08:00
|
|
|
for (int i = 0; i < argc; i += 1) {
|
2015-11-05 15:05:25 +08:00
|
|
|
char *arg = argv[i];
|
|
|
|
if (arg[0] == '-' && arg[1] == '-') {
|
2015-11-28 12:24:11 +08:00
|
|
|
if (strcmp(arg, "--release") == 0) {
|
2015-12-01 10:58:53 +08:00
|
|
|
b.release = true;
|
2015-11-25 13:32:26 +08:00
|
|
|
} else if (strcmp(arg, "--strip") == 0) {
|
2015-12-01 10:58:53 +08:00
|
|
|
b.strip = true;
|
2015-11-25 13:32:26 +08:00
|
|
|
} else if (strcmp(arg, "--static") == 0) {
|
2015-12-01 10:58:53 +08:00
|
|
|
b.is_static = true;
|
|
|
|
} else if (strcmp(arg, "--verbose") == 0) {
|
|
|
|
b.verbose = true;
|
2015-11-05 15:05:25 +08:00
|
|
|
} else if (i + 1 >= argc) {
|
|
|
|
return usage(arg0);
|
|
|
|
} else {
|
|
|
|
i += 1;
|
2015-12-01 16:06:10 +08:00
|
|
|
if (i >= argc) {
|
|
|
|
return usage(arg0);
|
|
|
|
} else if (strcmp(arg, "--output") == 0) {
|
2015-12-01 10:58:53 +08:00
|
|
|
b.out_file = argv[i];
|
2015-11-28 12:24:11 +08:00
|
|
|
} else if (strcmp(arg, "--export") == 0) {
|
|
|
|
if (strcmp(argv[i], "exe") == 0) {
|
2015-12-01 10:58:53 +08:00
|
|
|
b.out_type = OutTypeExe;
|
2015-11-28 12:24:11 +08:00
|
|
|
} else if (strcmp(argv[i], "lib") == 0) {
|
2015-12-01 10:58:53 +08:00
|
|
|
b.out_type = OutTypeLib;
|
2015-11-28 12:24:11 +08:00
|
|
|
} else if (strcmp(argv[i], "obj") == 0) {
|
2015-12-01 10:58:53 +08:00
|
|
|
b.out_type = OutTypeObj;
|
2015-11-28 12:24:11 +08:00
|
|
|
} else {
|
|
|
|
return usage(arg0);
|
|
|
|
}
|
2015-12-01 16:06:10 +08:00
|
|
|
} else if (strcmp(arg, "--color") == 0) {
|
|
|
|
if (strcmp(argv[i], "auto") == 0) {
|
|
|
|
b.color = ErrColorAuto;
|
|
|
|
} else if (strcmp(argv[i], "on") == 0) {
|
|
|
|
b.color = ErrColorOn;
|
|
|
|
} else if (strcmp(argv[i], "off") == 0) {
|
|
|
|
b.color = ErrColorOff;
|
|
|
|
} else {
|
|
|
|
return usage(arg0);
|
|
|
|
}
|
2015-11-28 12:24:11 +08:00
|
|
|
} else if (strcmp(arg, "--name") == 0) {
|
2015-12-01 10:58:53 +08:00
|
|
|
b.out_name = argv[i];
|
2015-12-16 03:44:42 +08:00
|
|
|
} else if (strcmp(arg, "--libc-path") == 0) {
|
|
|
|
b.libc_path = argv[i];
|
2015-11-05 15:05:25 +08:00
|
|
|
} else {
|
|
|
|
return usage(arg0);
|
|
|
|
}
|
|
|
|
}
|
2015-12-04 06:59:14 +08:00
|
|
|
} else if (!b.in_file) {
|
|
|
|
b.in_file = arg;
|
|
|
|
} else {
|
|
|
|
return usage(arg0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!b.in_file)
|
|
|
|
return usage(arg0);
|
|
|
|
|
|
|
|
Buf in_file_buf = BUF_INIT;
|
|
|
|
buf_init_from_str(&in_file_buf, b.in_file);
|
|
|
|
|
|
|
|
Buf root_source_dir = BUF_INIT;
|
|
|
|
Buf root_source_code = BUF_INIT;
|
|
|
|
Buf root_source_name = BUF_INIT;
|
|
|
|
if (buf_eql_str(&in_file_buf, "-")) {
|
|
|
|
os_get_cwd(&root_source_dir);
|
|
|
|
if ((err = os_fetch_file(stdin, &root_source_code))) {
|
|
|
|
fprintf(stderr, "unable to read stdin: %s\n", err_str(err));
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
buf_init_from_str(&root_source_name, "");
|
|
|
|
} else {
|
|
|
|
os_path_split(&in_file_buf, &root_source_dir, &root_source_name);
|
|
|
|
if ((err = os_fetch_file_path(buf_create_from_str(b.in_file), &root_source_code))) {
|
|
|
|
fprintf(stderr, "unable to open '%s': %s\n", b.in_file, err_str(err));
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
CodeGen *g = codegen_create(&root_source_dir);
|
|
|
|
codegen_set_build_type(g, b.release ? CodeGenBuildTypeRelease : CodeGenBuildTypeDebug);
|
|
|
|
codegen_set_strip(g, b.strip);
|
|
|
|
codegen_set_is_static(g, b.is_static);
|
|
|
|
if (b.out_type != OutTypeUnknown)
|
|
|
|
codegen_set_out_type(g, b.out_type);
|
|
|
|
if (b.out_name)
|
|
|
|
codegen_set_out_name(g, buf_create_from_str(b.out_name));
|
2015-12-16 03:44:42 +08:00
|
|
|
if (b.libc_path)
|
|
|
|
codegen_set_libc_path(g, buf_create_from_str(b.libc_path));
|
2015-12-04 06:59:14 +08:00
|
|
|
codegen_set_verbose(g, b.verbose);
|
|
|
|
codegen_set_errmsg_color(g, b.color);
|
2015-12-14 05:33:52 +08:00
|
|
|
codegen_add_root_code(g, &root_source_dir, &root_source_name, &root_source_code);
|
2015-12-04 06:59:14 +08:00
|
|
|
codegen_link(g, b.out_file);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-01-27 15:01:49 +08:00
|
|
|
struct ParseHPrint {
|
|
|
|
ParseH parse_h;
|
|
|
|
FILE *f;
|
|
|
|
int cur_indent;
|
|
|
|
};
|
|
|
|
|
|
|
|
static const int indent_size = 4;
|
|
|
|
|
|
|
|
static void print_indent(ParseHPrint *p) {
|
|
|
|
for (int i = 0; i < p->cur_indent; i += 1) {
|
|
|
|
fprintf(p->f, " ");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-27 18:11:51 +08:00
|
|
|
static Buf *type_node_to_name(AstNode *type_node) {
|
|
|
|
if (type_node->type == NodeTypeSymbol) {
|
|
|
|
return &type_node->data.symbol_expr.symbol;
|
|
|
|
} else if (type_node->type == NodeTypePrefixOpExpr) {
|
|
|
|
PrefixOp op = type_node->data.prefix_op_expr.prefix_op;
|
|
|
|
if (op == PrefixOpAddressOf) {
|
|
|
|
return buf_sprintf("&%s", buf_ptr(type_node_to_name(type_node->data.prefix_op_expr.primary_expr)));
|
|
|
|
} else {
|
|
|
|
zig_unreachable();
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
zig_unreachable();
|
|
|
|
}
|
2016-01-27 15:01:49 +08:00
|
|
|
}
|
|
|
|
|
2016-01-19 12:28:54 +08:00
|
|
|
static int parseh(const char *arg0, int argc, char **argv) {
|
|
|
|
char *in_file = nullptr;
|
|
|
|
ZigList<const char *> clang_argv = {0};
|
|
|
|
for (int i = 0; i < argc; i += 1) {
|
|
|
|
char *arg = argv[i];
|
|
|
|
if (arg[0] == '-') {
|
|
|
|
if (arg[1] == 'I') {
|
|
|
|
clang_argv.append(arg);
|
|
|
|
} else if (strcmp(arg, "-isystem") == 0) {
|
|
|
|
if (i + 1 >= argc) {
|
|
|
|
return usage(arg0);
|
|
|
|
}
|
|
|
|
clang_argv.append("-isystem");
|
|
|
|
clang_argv.append(argv[i + 1]);
|
|
|
|
} else if (arg[1] == 'B') {
|
|
|
|
clang_argv.append(arg);
|
|
|
|
} else {
|
|
|
|
fprintf(stderr, "unrecognized argument: %s", arg);
|
|
|
|
return usage(arg0);
|
|
|
|
}
|
|
|
|
} else if (!in_file) {
|
|
|
|
in_file = arg;
|
|
|
|
} else {
|
|
|
|
return usage(arg0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!in_file) {
|
|
|
|
fprintf(stderr, "missing target argument");
|
|
|
|
return usage(arg0);
|
|
|
|
}
|
|
|
|
|
2016-01-27 15:01:49 +08:00
|
|
|
clang_argv.append(in_file);
|
|
|
|
|
2016-01-27 18:11:51 +08:00
|
|
|
Buf *libc_include_path = buf_alloc();
|
|
|
|
os_path_join(buf_create_from_str(ZIG_LIBC_DIR), buf_create_from_str("include"), libc_include_path);
|
|
|
|
clang_argv.append("-isystem");
|
|
|
|
clang_argv.append(buf_ptr(libc_include_path));
|
|
|
|
|
2016-01-27 15:01:49 +08:00
|
|
|
ParseHPrint parse_h_print = {{{0}}};
|
|
|
|
ParseHPrint *p = &parse_h_print;
|
|
|
|
p->f = stdout;
|
|
|
|
p->cur_indent = 0;
|
|
|
|
|
|
|
|
parse_h_file(&p->parse_h, &clang_argv);
|
|
|
|
|
|
|
|
if (p->parse_h.errors.length > 0) {
|
|
|
|
for (int i = 0; i < p->parse_h.errors.length; i += 1) {
|
|
|
|
ErrorMsg *err_msg = p->parse_h.errors.at(i);
|
|
|
|
// TODO respect --color arg
|
|
|
|
print_err_msg(err_msg, ErrColorAuto);
|
|
|
|
}
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int struct_i = 0; struct_i < p->parse_h.struct_list.length; struct_i += 1) {
|
|
|
|
AstNode *struct_decl = p->parse_h.struct_list.at(struct_i);
|
|
|
|
assert(struct_decl->type == NodeTypeStructDecl);
|
|
|
|
const char *struct_name = buf_ptr(&struct_decl->data.struct_decl.name);
|
|
|
|
print_indent(p);
|
|
|
|
fprintf(p->f, "struct %s {\n", struct_name);
|
|
|
|
p->cur_indent += indent_size;
|
|
|
|
for (int field_i = 0; field_i < struct_decl->data.struct_decl.fields.length; field_i += 1) {
|
|
|
|
AstNode *field_node = struct_decl->data.struct_decl.fields.at(field_i);
|
|
|
|
assert(field_node->type == NodeTypeStructField);
|
|
|
|
const char *field_name = buf_ptr(&field_node->data.struct_field.name);
|
2016-01-27 18:11:51 +08:00
|
|
|
Buf *type_name = type_node_to_name(field_node->data.struct_field.type);
|
2016-01-27 15:01:49 +08:00
|
|
|
print_indent(p);
|
2016-01-27 18:11:51 +08:00
|
|
|
fprintf(p->f, "%s: %s,\n", field_name, buf_ptr(type_name));
|
2016-01-27 15:01:49 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
p->cur_indent -= indent_size;
|
|
|
|
fprintf(p->f, "}\n\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int fn_i = 0; fn_i < p->parse_h.fn_list.length; fn_i += 1) {
|
|
|
|
AstNode *fn_proto = p->parse_h.fn_list.at(fn_i);
|
|
|
|
assert(fn_proto->type == NodeTypeFnProto);
|
|
|
|
print_indent(p);
|
|
|
|
const char *fn_name = buf_ptr(&fn_proto->data.fn_proto.name);
|
2016-01-27 18:11:51 +08:00
|
|
|
const char *pub_str = (fn_proto->data.fn_proto.visib_mod == VisibModPub) ? "pub " : "";
|
|
|
|
fprintf(p->f, "%sextern fn %s(", pub_str, fn_name);
|
2016-01-27 15:01:49 +08:00
|
|
|
int arg_count = fn_proto->data.fn_proto.params.length;
|
|
|
|
bool is_var_args = fn_proto->data.fn_proto.is_var_args;
|
|
|
|
for (int arg_i = 0; arg_i < arg_count; arg_i += 1) {
|
|
|
|
AstNode *param_decl = fn_proto->data.fn_proto.params.at(arg_i);
|
|
|
|
assert(param_decl->type == NodeTypeParamDecl);
|
|
|
|
const char *arg_name = buf_ptr(¶m_decl->data.param_decl.name);
|
2016-01-27 18:11:51 +08:00
|
|
|
Buf *arg_type = type_node_to_name(param_decl->data.param_decl.type);
|
|
|
|
const char *noalias_str = param_decl->data.param_decl.is_noalias ? "noalias " : "";
|
|
|
|
fprintf(p->f, "%s%s: %s", noalias_str, arg_name, buf_ptr(arg_type));
|
2016-01-27 15:01:49 +08:00
|
|
|
if (arg_i + 1 < arg_count || is_var_args) {
|
|
|
|
fprintf(p->f, ", ");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (is_var_args) {
|
|
|
|
fprintf(p->f, "...");
|
|
|
|
}
|
|
|
|
fprintf(p->f, ")");
|
2016-01-27 18:11:51 +08:00
|
|
|
Buf *return_type_name = type_node_to_name(fn_proto->data.fn_proto.return_type);
|
|
|
|
if (!buf_eql_str(return_type_name, "void")) {
|
|
|
|
fprintf(p->f, " -> %s", buf_ptr(return_type_name));
|
2016-01-27 15:01:49 +08:00
|
|
|
}
|
|
|
|
fprintf(p->f, ";\n");
|
|
|
|
}
|
|
|
|
|
2016-01-19 12:28:54 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-12-04 06:59:14 +08:00
|
|
|
int main(int argc, char **argv) {
|
|
|
|
char *arg0 = argv[0];
|
|
|
|
int (*cmd)(const char *, int, char **) = nullptr;
|
|
|
|
for (int i = 1; i < argc; i += 1) {
|
|
|
|
char *arg = argv[i];
|
|
|
|
if (arg[0] == '-' && arg[1] == '-') {
|
2015-11-05 15:05:25 +08:00
|
|
|
return usage(arg0);
|
2015-12-04 06:59:14 +08:00
|
|
|
} else {
|
|
|
|
if (strcmp(arg, "build") == 0) {
|
|
|
|
cmd = build;
|
|
|
|
} else if (strcmp(arg, "version") == 0) {
|
|
|
|
cmd = version;
|
2016-01-19 12:28:54 +08:00
|
|
|
} else if (strcmp(arg, "parseh") == 0) {
|
|
|
|
cmd = parseh;
|
2015-12-04 06:59:14 +08:00
|
|
|
} else {
|
|
|
|
fprintf(stderr, "Unrecognized command: %s\n", arg);
|
|
|
|
return usage(arg0);
|
|
|
|
}
|
|
|
|
return cmd(arg0, argc - i - 1, &argv[i + 1]);
|
|
|
|
}
|
2015-11-05 15:05:25 +08:00
|
|
|
}
|
2015-11-05 15:06:36 +08:00
|
|
|
|
2015-12-04 06:59:14 +08:00
|
|
|
return usage(arg0);
|
2015-11-05 15:05:25 +08:00
|
|
|
}
|