#import "Basic"; #import "Compiler"; #import "File"; #import "Process"; #import "Autorun"; #placeholder MEMORY_DEBUGGER_ENABLED; #placeholder IS_DEVELOPER; #placeholder IS_UA_ALLOCATOR; program_args: [..]string; /** TODO: Different parsing regime. This is very ugly to maintain, maybe we just use `Command_Line` here. */ args_help: bool; args_compiler_silent: bool; args_program_run: bool; args_build_release: bool; args_build_lib: bool; args_memory_debug: bool; args_dev_server: bool; args_dev_server_only: bool; args_gen: bool; args_search: bool; args_fuzz_request: bool; args_fuzz_e2e: bool; args_fuzzer: bool; args_tests: bool; args_um_alloc: bool; build :: () { args := get_build_options().compile_time_command_line; // args args_help = array_find(args, "help"); args_compiler_silent = array_find(args, "silent"); args_program_run = array_find(args, "run"); args_build_release = array_find(args, "release"); args_build_lib = array_find(args, "lib"); args_memory_debug = array_find(args, "memory"); args_dev_server = array_find(args, "server"); args_dev_server_only = array_find(args, "oserver"); args_gen = array_find(args, "gen"); args_search = array_find(args, "search"); args_fuzz_request = array_find(args, "request"); args_fuzz_e2e = array_find(args, "e2e"); args_fuzzer = array_find(args, "fuzzer"); args_tests = array_find(args, "tests"); args_um_alloc = array_find(args, "unmap"); // program args program_args = program_args_collect(args); // @OS GC // ----------------------------------------- prog_gen := compiler_create_workspace(); if !prog_gen { log("Workspace creation failed: Gen"); return; } prog_search := compiler_create_workspace(); if !prog_search { log("Workspace creation failed: Search"); return; } prog_fuzzer := compiler_create_workspace(); if !prog_fuzzer { log("Workspace creation failed: Fuzzer"); return; } prog_tests := compiler_create_workspace(); if !prog_tests { log("Workspace creation failed: Tests"); return; } prog_store := compiler_create_workspace(); if !prog_store { log("Workspace creation failed: store"); return; } set_build_options_dc(.{ do_output = false }); if args_help { args_help_print(); return; } if args_dev_server || args_dev_server_only { dev_server_run(); if args_dev_server_only return; } make_directory_if_it_does_not_exist("bin"); make_directory_if_it_does_not_exist("bin/debug"); make_directory_if_it_does_not_exist("bin/release"); make_directory_if_it_does_not_exist("bin/lib"); if args_fuzzer { if args_search { corpus_dir :: "fuzzer/corpus_search/"; code :: #code { add_build_file(tprint("%src/fuzzer/fuzzer_search.jai", #filepath), w); } fuzzer_run_pipeline(prog_fuzzer, "fuzzer_search", corpus_dir, intercept=code); } else if args_fuzz_request { corpus_dir :: "fuzzer/corpus_request/"; code :: #code { add_build_file(tprint("%src/fuzzer/fuzzer_request.jai", #filepath), w); } fuzzer_run_pipeline(prog_fuzzer, "fuzzer_request", corpus_dir, intercept=code); } else if args_fuzz_e2e { corpus_dir :: "fuzzer/corpus_e2e/"; code :: #code { add_build_file(tprint("%src/fuzzer/fuzzer_e2e.jai", #filepath), w); } fuzzer_run_pipeline(prog_fuzzer, "fuzzer_e2e", corpus_dir, intercept=code); } } else if args_gen { workspace_setup(prog_gen, "generator", #code { add_build_file(tprint("%src/gen/main.jai", #filepath), w); add_build_string( tprint("MEMORY_DEBUGGER_ENABLED :: %;", args_memory_debug), w ); }); } else if args_search { workspace_setup(prog_search, "search", #code { add_build_file(tprint("%src/search/main.jai", #filepath), w); add_build_string( tprint("MEMORY_DEBUGGER_ENABLED :: %;", args_memory_debug), w ); }); } else if args_tests { workspace_setup(prog_tests, "tests", #code { add_build_file(tprint("%tests/main.jai", #filepath), w); }); } } build_debug :: (w: Workspace, target_options: *Build_Options) { log("Choosing debug options ..."); target_options.backend =.X64; target_options.output_path = "bin/debug"; set_optimization(target_options, Optimization_Type.DEBUG, true); set_build_options(target_options.*, w); } build_release :: (w: Workspace, target_options: *Build_Options) { log("Choosing release options ..."); target_options.backend = .LLVM; target_options.output_path = "bin/release"; target_options.stack_trace = false; target_options.backtrace_on_crash = .OFF; target_options.emit_debug_info = .DEFAULT; set_optimization(target_options, Optimization_Type.VERY_OPTIMIZED); set_build_options(target_options.*, w); } build_lib_static :: (w: Workspace, target_options: *Build_Options) { log("Choosing dyn lib options ..."); target_options.backend = .LLVM; target_options.output_type = .DYNAMIC_LIBRARY; target_options.output_path = "bin/lib"; set_optimization(target_options, Optimization_Type.VERY_OPTIMIZED); set_build_options(target_options.*, w); } workspace_setup :: (w: Workspace, program_name: string, $intercept: Code) #expand { print("The workspace w is %\n", w); target_options := get_build_options(w); target_options.output_executable_name = program_name; if args_compiler_silent target_options.text_output_flags = 0; if args_build_release { build_release(w, *target_options); } else if args_build_lib { build_lib_static(w, *target_options); } else { build_debug(w, *target_options); } compiler_begin_intercept(w); #insert,scope() intercept; if args_build_release { add_build_string("IS_DEVELOPER :: false;", w); } else { add_build_string("IS_DEVELOPER :: true;", w); } if args_um_alloc { add_build_string("IS_UA_ALLOCATOR :: true;", w); } else { add_build_string("IS_UA_ALLOCATOR :: false;", w); } compiler_response := message_loop(); compiler_end_intercept(w); if !compiler_response { log("Compiler response failed."); return; } if args_program_run run_build_result_of_workspace(w, program_args); } dev_server_run :: () { cmd :: string.[ "python", "dev/server.py" ]; run_command(..cmd); } fuzzer_run_pipeline :: ( w: Workspace, program_name: string, corpus_dir: string, runs := 1_000_000, $intercept: Code ) { log("The workspace w is %", w); target_options := get_build_options(w); target_options.output_executable_name = program_name; target_options.text_output_flags = 0; fp_exec := tprint("fuzzer/%", program_name); build_lib_static(w, *target_options); compiler_begin_intercept(w); #insert,scope() intercept; compiler_response := message_loop(); compiler_end_intercept(w); if !compiler_response { log("Compiler response failed."); return; } log("Building fuzzer via clang ..."); clang_cmd := string.[ "clang++", "-fsanitize=fuzzer,address", tprint("bin/lib/%.so", program_name), "-o", fp_exec ]; run_command(..clang_cmd); fuzz_cmd := string.[ fp_exec, tprint("-runs=%", runs), "-max_total_time=60", "-rss_limit_mb=512", "-max_total_time=60", "-artifact_prefix=fuzzer/", corpus_dir ]; if args_program_run then run_command(..fuzz_cmd); } program_args_collect :: (args: []string, divider: string = "::") -> [..]string { buf: [..]string; success, match := array_find(args, divider); if success for i: match+1..args.count-1 array_add(*buf, args[i]); return buf; } message_loop :: () -> ok: bool { while true { message := compiler_wait_for_message(); if !message break; if message.kind == { case .COMPLETE; message_complete := cast(*Message_Complete) message; return message_complete.error_code == 0; } } return false; } args_help_print :: () { help_message :: #string _END_ Usage: jai build.jai - [OPTIONS] :: [PROGRAM ARGS] Options: help Prints this help menu. silent Disables compiler/linker statistics. run Runs your program afterwards. release Builds with release options. If omitted, it builds a debug build. memory Enables the memory leak detector. Passing Args to your Program: If you want to supply args to your program, pass it like that: `jai build.jai - run :: my_arg1 foo bar abc ABC` Everything after the `::` get's forwarded to your program. _END_; log(help_message); } main :: () {} #run build();