/* * Copyright (c) 2015 Andrew Kelley * * This file is part of zig, which is MIT licensed. * See http://opensource.org/licenses/MIT */ #include "ast_render.hpp" #include "buffer.hpp" #include "codegen.hpp" #include "config.h" #include "error.hpp" #include "link.hpp" #include "os.hpp" #include "target.hpp" #include static int usage(const char *arg0) { fprintf(stderr, "Usage: %s [command] [options]\n" "Commands:\n" " build build project from build.zig\n" " build_exe [source] create executable from source\n" " build_lib [source] create library from source\n" " build_obj [source] create object from source\n" " parseh [source] convert a c header file to zig extern declarations\n" " targets list available compilation targets\n" " test [source] create and run a test build\n" " version print version number and exit\n" "Options:\n" " --ar-path [path] set the path to ar\n" " --color [auto|off|on] enable or disable colored error messages\n" " --dynamic-linker [path] set the path to ld.so\n" " --each-lib-rpath add rpath for each used dynamic library\n" " --ld-path [path] set the path to the linker\n" " --libc-include-dir [path] directory where libc stdlib.h resides\n" " --libc-lib-dir [path] directory where libc crt1.o resides\n" " --libc-static-lib-dir [path] directory where libc crtbegin.o resides\n" " --library [lib] link against lib\n" " --library-path [dir] add a directory to the library search path\n" " --linker-script [path] use a custom linker script\n" " --name [name] override output name\n" " --output [file] override destination path\n" " --release build with optimizations on and debug protection off\n" " --static output will be statically linked\n" " --strip exclude debug symbols\n" " --target-arch [name] specify target architecture\n" " --target-environ [name] specify target environment\n" " --target-os [name] specify target operating system\n" " --verbose turn on compiler debug output\n" " --zig-std-dir [path] directory where zig standard library resides\n" " -L[dir] alias for --library-path\n" " -dirafter [dir] same as -isystem but do it last\n" " -framework [name] (darwin only) link against framework\n" " -isystem [dir] add additional search path for other .h files\n" " -mconsole (windows only) --subsystem console to the linker\n" " -mios-version-min [ver] (darwin only) set iOS deployment target\n" " -mlinker-version [ver] (darwin only) override linker version\n" " -mmacosx-version-min [ver] (darwin only) set Mac OS X deployment target\n" " -municode (windows only) link with unicode\n" " -mwindows (windows only) --subsystem windows to the linker\n" " -rdynamic add all symbols to the dynamic symbol table\n" " -rpath [path] add directory to the runtime library search path\n" , arg0); return EXIT_FAILURE; } static int print_target_list(FILE *f) { ZigTarget native; get_native_target(&native); fprintf(f, "Architectures:\n"); size_t arch_count = target_arch_count(); for (size_t arch_i = 0; arch_i < arch_count; arch_i += 1) { const ArchType *arch = get_target_arch(arch_i); char arch_name[50]; get_arch_name(arch_name, arch); const char *native_str = (native.arch.arch == arch->arch && native.arch.sub_arch == arch->sub_arch) ? " (native)" : ""; fprintf(f, " %s%s\n", arch_name, native_str); } fprintf(f, "\nOperating Systems:\n"); size_t os_count = target_os_count(); for (size_t i = 0; i < os_count; i += 1) { ZigLLVM_OSType os_type = get_target_os(i); const char *native_str = (native.os == os_type) ? " (native)" : ""; fprintf(f, " %s%s\n", get_target_os_name(os_type), native_str); } fprintf(f, "\nEnvironments:\n"); size_t environ_count = target_environ_count(); for (size_t i = 0; i < environ_count; i += 1) { ZigLLVM_EnvironmentType environ_type = get_target_environ(i); const char *native_str = (native.env_type == environ_type) ? " (native)" : ""; fprintf(f, " %s%s\n", ZigLLVMGetEnvironmentTypeName(environ_type), native_str); } return EXIT_SUCCESS; } enum Cmd { CmdInvalid, CmdBuild, CmdTest, CmdVersion, CmdParseH, CmdTargets, }; int main(int argc, char **argv) { os_init(); char *arg0 = argv[0]; Cmd cmd = CmdInvalid; const char *in_file = nullptr; const char *out_file = nullptr; bool is_release_build = false; bool strip = false; bool is_static = false; OutType out_type = OutTypeUnknown; const char *out_name = nullptr; bool verbose = false; ErrColor color = ErrColorAuto; const char *libc_lib_dir = nullptr; const char *libc_static_lib_dir = nullptr; const char *libc_include_dir = nullptr; const char *zig_std_dir = nullptr; const char *dynamic_linker = nullptr; ZigList clang_argv = {0}; ZigList lib_dirs = {0}; ZigList link_libs = {0}; ZigList frameworks = {0}; int err; const char *target_arch = nullptr; const char *target_os = nullptr; const char *target_environ = nullptr; bool mwindows = false; bool mconsole = false; bool municode = false; const char *mlinker_version = nullptr; bool rdynamic = false; const char *mmacosx_version_min = nullptr; const char *mios_version_min = nullptr; const char *linker_script = nullptr; ZigList rpath_list = {0}; bool each_lib_rpath = false; if (argc >= 2 && strcmp(argv[1], "build") == 0) { const char *zig_exe_path = arg0; init_all_targets(); Buf *zig_std_dir = buf_create_from_str(ZIG_STD_DIR); Buf *special_dir = buf_alloc(); os_path_join(zig_std_dir, buf_sprintf("special"), special_dir); Buf *build_runner_path = buf_alloc(); os_path_join(special_dir, buf_create_from_str("build_runner.zig"), build_runner_path); ZigList args = {0}; args.append(zig_exe_path); for (int i = 2; i < argc; i += 1) { if (strcmp(argv[i], "--verbose") == 0) { verbose = true; args.append(argv[i]); } else { args.append(argv[i]); } } Buf root_source_dir = BUF_INIT; Buf root_source_code = BUF_INIT; Buf root_source_name = BUF_INIT; os_path_split(build_runner_path, &root_source_dir, &root_source_name); if ((err = os_fetch_file_path(build_runner_path, &root_source_code))) { fprintf(stderr, "unable to open '%s': %s\n", buf_ptr(build_runner_path), err_str(err)); return 1; } CodeGen *g = codegen_create(&root_source_dir, nullptr); codegen_set_out_name(g, buf_create_from_str("build")); codegen_set_out_type(g, OutTypeExe); codegen_set_verbose(g, verbose); PackageTableEntry *build_pkg = new_package(".", "build.zig"); build_pkg->package_table.put(buf_create_from_str("std"), g->std_package); g->root_package->package_table.put(buf_create_from_str("@build"), build_pkg); codegen_add_root_code(g, &root_source_dir, &root_source_name, &root_source_code); codegen_link(g, "build"); Termination term; os_spawn_process("./build", args, &term); if (term.how != TerminationIdClean || term.code != 0) { fprintf(stderr, "\nBuild failed. Use the following command to reproduce the failure:\n"); fprintf(stderr, "./build"); for (size_t i = 0; i < args.length; i += 1) { fprintf(stderr, " %s", args.at(i)); } fprintf(stderr, "\n"); } else { os_delete_file(buf_create_from_str("./build")); } return (term.how == TerminationIdClean) ? term.code : -1; } for (int i = 1; i < argc; i += 1) { char *arg = argv[i]; if (arg[0] == '-') { if (strcmp(arg, "--release") == 0) { is_release_build = true; } else if (strcmp(arg, "--strip") == 0) { strip = true; } else if (strcmp(arg, "--static") == 0) { is_static = true; } else if (strcmp(arg, "--verbose") == 0) { verbose = true; } else if (strcmp(arg, "-mwindows") == 0) { mwindows = true; } else if (strcmp(arg, "-mconsole") == 0) { mconsole = true; } else if (strcmp(arg, "-municode") == 0) { municode = true; } else if (strcmp(arg, "-rdynamic") == 0) { rdynamic = true; } else if (strcmp(arg, "--each-lib-rpath") == 0) { each_lib_rpath = true; } else if (arg[1] == 'L' && arg[2] != 0) { // alias for --library-path lib_dirs.append(&arg[2]); } else if (i + 1 >= argc) { return usage(arg0); } else { i += 1; if (i >= argc) { return usage(arg0); } else if (strcmp(arg, "--output") == 0) { out_file = argv[i]; } else if (strcmp(arg, "--color") == 0) { if (strcmp(argv[i], "auto") == 0) { color = ErrColorAuto; } else if (strcmp(argv[i], "on") == 0) { color = ErrColorOn; } else if (strcmp(argv[i], "off") == 0) { color = ErrColorOff; } else { return usage(arg0); } } else if (strcmp(arg, "--name") == 0) { out_name = argv[i]; } else if (strcmp(arg, "--libc-lib-dir") == 0) { libc_lib_dir = argv[i]; } else if (strcmp(arg, "--libc-static-lib-dir") == 0) { libc_static_lib_dir = argv[i]; } else if (strcmp(arg, "--libc-include-dir") == 0) { libc_include_dir = argv[i]; } else if (strcmp(arg, "--zig-std-dir") == 0) { zig_std_dir = argv[i]; } else if (strcmp(arg, "--dynamic-linker") == 0) { dynamic_linker = argv[i]; } else if (strcmp(arg, "-isystem") == 0) { clang_argv.append("-isystem"); clang_argv.append(argv[i]); } else if (strcmp(arg, "-dirafter") == 0) { clang_argv.append("-dirafter"); clang_argv.append(argv[i]); } else if (strcmp(arg, "--library-path") == 0 || strcmp(arg, "-L") == 0) { lib_dirs.append(argv[i]); } else if (strcmp(arg, "--library") == 0) { link_libs.append(argv[i]); } else if (strcmp(arg, "--target-arch") == 0) { target_arch = argv[i]; } else if (strcmp(arg, "--target-os") == 0) { target_os = argv[i]; } else if (strcmp(arg, "--target-environ") == 0) { target_environ = argv[i]; } else if (strcmp(arg, "-mlinker-version") == 0) { mlinker_version = argv[i]; } else if (strcmp(arg, "-mmacosx-version-min") == 0) { mmacosx_version_min = argv[i]; } else if (strcmp(arg, "-mios-version-min") == 0) { mios_version_min = argv[i]; } else if (strcmp(arg, "-framework") == 0) { frameworks.append(argv[i]); } else if (strcmp(arg, "--linker-script") == 0) { linker_script = argv[i]; } else if (strcmp(arg, "-rpath") == 0) { rpath_list.append(argv[i]); } else { fprintf(stderr, "Invalid argument: %s\n", arg); return usage(arg0); } } } else if (cmd == CmdInvalid) { if (strcmp(arg, "build_exe") == 0) { cmd = CmdBuild; out_type = OutTypeExe; } else if (strcmp(arg, "build_obj") == 0) { cmd = CmdBuild; out_type = OutTypeObj; } else if (strcmp(arg, "build_lib") == 0) { cmd = CmdBuild; out_type = OutTypeLib; } else if (strcmp(arg, "version") == 0) { cmd = CmdVersion; } else if (strcmp(arg, "parseh") == 0) { cmd = CmdParseH; } else if (strcmp(arg, "test") == 0) { cmd = CmdTest; } else if (strcmp(arg, "targets") == 0) { cmd = CmdTargets; } else { fprintf(stderr, "Unrecognized command: %s\n", arg); return usage(arg0); } } else { switch (cmd) { case CmdBuild: case CmdParseH: case CmdTest: if (!in_file) { in_file = arg; } else { return usage(arg0); } break; case CmdVersion: case CmdTargets: return usage(arg0); case CmdInvalid: zig_unreachable(); } } } switch (cmd) { case CmdBuild: case CmdParseH: case CmdTest: { if (!in_file) return usage(arg0); assert(cmd != CmdBuild || out_type != OutTypeUnknown); init_all_targets(); ZigTarget alloc_target; ZigTarget *target; if (!target_arch && !target_os && !target_environ) { target = nullptr; } else { target = &alloc_target; get_unknown_target(target); if (target_arch) { if (parse_target_arch(target_arch, &target->arch)) { fprintf(stderr, "invalid --target-arch argument\n"); return usage(arg0); } } if (target_os) { if (parse_target_os(target_os, &target->os)) { fprintf(stderr, "invalid --target-os argument\n"); return usage(arg0); } } if (target_environ) { if (parse_target_environ(target_environ, &target->env_type)) { fprintf(stderr, "invalid --target-environ argument\n"); return usage(arg0); } } } Buf in_file_buf = BUF_INIT; buf_init_from_str(&in_file_buf, in_file); Buf root_source_dir = BUF_INIT; Buf root_source_code = BUF_INIT; Buf root_source_name = BUF_INIT; Buf *buf_out_name = (cmd == CmdTest) ? buf_create_from_str("test") : (out_name == nullptr) ? nullptr : buf_create_from_str(out_name); 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(in_file), &root_source_code))) { fprintf(stderr, "unable to open '%s': %s\n", in_file, err_str(err)); return 1; } if (cmd == CmdBuild && buf_out_name == nullptr) { buf_out_name = buf_alloc(); Buf ext_name = BUF_INIT; os_path_extname(&root_source_name, buf_out_name, &ext_name); } } if (cmd == CmdBuild && buf_out_name == nullptr) { fprintf(stderr, "--name [name] not provided and unable to infer\n\n"); return usage(arg0); } CodeGen *g = codegen_create(&root_source_dir, target); codegen_set_is_release(g, is_release_build); codegen_set_is_test(g, cmd == CmdTest); codegen_set_linker_script(g, linker_script); if (each_lib_rpath) codegen_set_each_lib_rpath(g, each_lib_rpath); codegen_set_clang_argv(g, clang_argv.items, clang_argv.length); codegen_set_strip(g, strip); codegen_set_is_static(g, is_static); if (out_type != OutTypeUnknown) { codegen_set_out_type(g, out_type); } else if (cmd == CmdTest) { codegen_set_out_type(g, OutTypeExe); } codegen_set_out_name(g, buf_out_name); if (libc_lib_dir) codegen_set_libc_lib_dir(g, buf_create_from_str(libc_lib_dir)); if (libc_static_lib_dir) codegen_set_libc_static_lib_dir(g, buf_create_from_str(libc_static_lib_dir)); if (libc_include_dir) codegen_set_libc_include_dir(g, buf_create_from_str(libc_include_dir)); if (zig_std_dir) codegen_set_zig_std_dir(g, buf_create_from_str(zig_std_dir)); if (dynamic_linker) codegen_set_dynamic_linker(g, buf_create_from_str(dynamic_linker)); codegen_set_verbose(g, verbose); codegen_set_errmsg_color(g, color); for (size_t i = 0; i < lib_dirs.length; i += 1) { codegen_add_lib_dir(g, lib_dirs.at(i)); } for (size_t i = 0; i < link_libs.length; i += 1) { codegen_add_link_lib(g, link_libs.at(i)); } for (size_t i = 0; i < frameworks.length; i += 1) { codegen_add_framework(g, frameworks.at(i)); } for (size_t i = 0; i < rpath_list.length; i += 1) { codegen_add_rpath(g, rpath_list.at(i)); } codegen_set_windows_subsystem(g, mwindows, mconsole); codegen_set_windows_unicode(g, municode); codegen_set_rdynamic(g, rdynamic); if (mlinker_version) { codegen_set_mlinker_version(g, buf_create_from_str(mlinker_version)); } if (mmacosx_version_min && mios_version_min) { fprintf(stderr, "-mmacosx-version-min and -mios-version-min options not allowed together\n"); return EXIT_FAILURE; } if (mmacosx_version_min) { codegen_set_mmacosx_version_min(g, buf_create_from_str(mmacosx_version_min)); } if (mios_version_min) { codegen_set_mios_version_min(g, buf_create_from_str(mios_version_min)); } if (cmd == CmdBuild) { codegen_add_root_code(g, &root_source_dir, &root_source_name, &root_source_code); codegen_link(g, out_file); return EXIT_SUCCESS; } else if (cmd == CmdParseH) { codegen_parseh(g, &root_source_dir, &root_source_name, &root_source_code); ast_render_decls(stdout, 4, g->root_import); return EXIT_SUCCESS; } else if (cmd == CmdTest) { codegen_add_root_code(g, &root_source_dir, &root_source_name, &root_source_code); codegen_link(g, "./test"); ZigList args = {0}; Termination term; os_spawn_process("./test", args, &term); if (term.how != TerminationIdClean || term.code != 0) { fprintf(stderr, "\nTests failed. Use the following command to reproduce the failure:\n"); fprintf(stderr, "./test\n"); } return (term.how == TerminationIdClean) ? term.code : -1; } else { zig_unreachable(); } } case CmdVersion: printf("%s\n", ZIG_VERSION_STRING); return EXIT_SUCCESS; case CmdTargets: return print_target_list(stdout); case CmdInvalid: return usage(arg0); } }