#include "build/build.h" #include "build_internal.h" #include "project.h" #include "utils/json.h" #include "utils/lib.h" #define PRINTFN(string, ...) fprintf(stdout, string "\n", ##__VA_ARGS__) // NOLINT #define PRINTF(string, ...) fprintf(stdout, string, ##__VA_ARGS__) // NOLINT const char** get_project_dependency_directories() { const char *filename; JSONObject *json = project_json_load(&filename); const char *target = NULL; const char **deps_dirs = NULL; BuildParseContext context = { filename, target }; APPEND_STRING_LIST(&deps_dirs, "dependency-search-paths"); return deps_dirs; } static void print_vec(const char *header, const char **vec, bool opt, const char *delim) { if (opt && !vec) return; if (header[0] != '\0') PRINTF("%s: ", header); if (!vec_size(vec)) { PRINTFN("*none*"); return; } FOREACH_IDX(i, const char *, item, vec) { if (i > 0) PRINTF("%s", delim); PRINTF("%s", item); } PRINTFN(""); } const char** get_project_dependencies() { const char *filename; const char** dependencies = NULL; JSONObject *project_json = project_json_load(&filename); JSONObject *dependencies_json = json_map_get(project_json, "dependencies"); FOREACH(JSONObject *, element, dependencies_json->elements) { vec_add(dependencies, element->str); } return dependencies; } static void print_opt_str(const char *header, const char *str) { if (!str) return; if (header[0] != '\0') PRINTF("%s: ", header); PRINTFN("%s", str); } static void print_opt_setting(const char *header, int setting, const char **values) { if (setting < 0) return; if (header[0] != '\0') PRINTF("%s: ", header); PRINTFN("%s", values[setting]); } static void print_opt_bool(const char *header, int b) { if (b == -1) return; if (header[0] != '\0') PRINTF("%s: ", header); PRINTFN(b ? "true" : "false"); } static void print_opt_int(const char *header, long v) { if (v < 0) return; if (header[0] != '\0') PRINTF("%s: ", header); PRINTFN("%ld", v); } static const char *generate_expected(const char **options, size_t n) { scratch_buffer_clear(); for (size_t i = 0; i < n; i++) { if (i > 0) scratch_buffer_append(", "); if (i == n - 1) scratch_buffer_append(" or "); scratch_buffer_printf("\"%s\"", options[i]); } return scratch_buffer_to_string(); } const char *optimization_levels[] = { [OPT_SETTING_O0] = "O0", [OPT_SETTING_O1] = "O1", [OPT_SETTING_O2] = "O2", [OPT_SETTING_O3] = "O3", [OPT_SETTING_O4] = "O4", [OPT_SETTING_O5] = "O5", [OPT_SETTING_OSMALL] = "Os", [OPT_SETTING_OTINY] = "Oz" }; const char *debug_levels[] = { [DEBUG_INFO_NONE] = "none", [DEBUG_INFO_LINE_TABLES] = "line-tables", [DEBUG_INFO_FULL] = "full" }; #define VIEW_MANDATORY_STRING_ARRAY(header, key, delim) \ do { \ const char** arr = get_string_array(context, project_json, key, true);\ print_vec(header, arr, false, delim);\ } while(0) #define VIEW_STRING_ARRAY(header, key, delim) \ do { \ const char** arr = get_optional_string_array(context, project_json, key);\ print_vec(header, arr, true, delim);\ } while(0) #define VIEW_MANDATORY_STRING(header, key) \ do { \ const char* str = get_mandatory_string(context, project_json, key);\ print_opt_str(header, str);\ } while(0) #define VIEW_STRING(header, key) \ do { \ const char* str = get_optional_string(context, project_json, key);\ print_opt_str(header, str);\ } while(0) #define VIEW_SETTING(header, key, expected_arr) \ do { \ int setting = get_valid_string_setting(context, project_json, key, expected_arr, 0, ELEMENTLEN(expected_arr), generate_expected(expected_arr, ELEMENTLEN(expected_arr)));\ print_opt_setting(header, setting, expected_arr);\ } while(0) #define VIEW_BOOL(header, key) \ do {\ int val = get_valid_bool(context, project_json, key, -1);\ print_opt_bool(header, val);\ } while(0) #define VIEW_INTEGER(header, key) \ do {\ long v = get_valid_integer(context, project_json, context.file, false);\ print_opt_int(header, v);\ } while(0); #define TARGET_VIEW_STRING_ARRAY(header, key, delim) \ do {\ const char** arr = get_optional_string_array(context, target, key);\ print_vec("\t" header, arr, true, delim);\ } while(0) #define TARGET_VIEW_MANDATORY_STRING(header, key) \ do { \ const char* str = get_mandatory_string(context, target, key);\ print_opt_str("\t" header, str);\ } while(0) #define TARGET_VIEW_STRING(header, key) \ do { \ const char* str = get_optional_string(context, target, key);\ print_opt_str("\t" header, str);\ } while(0) #define TARGET_VIEW_SETTING(header, key, expected_arr) \ do { \ int setting = get_valid_string_setting(context, target, key, expected_arr, 0, ELEMENTLEN(expected_arr), generate_expected(expected_arr, ELEMENTLEN(expected_arr)));\ print_opt_setting("\t" header, setting, expected_arr);\ } while(0) #define TARGET_VIEW_BOOL(header, key) \ do {\ int val = get_valid_bool(context, target, key, -1);\ print_opt_bool("\t" header, val);\ } while(0) #define TARGET_VIEW_INTEGER(header, key) \ do {\ long v = get_valid_integer(context, target, key, false);\ print_opt_int("\t" header, v);\ } while(0); static void view_target(BuildParseContext context, JSONObject *target, bool verbose) { if (!verbose) { PRINTFN("%s", context.target); return; } /* General target information */ PRINTFN("- %s", context.target); print_opt_str("\tName", context.target); TARGET_VIEW_MANDATORY_STRING("Type", "type"); TARGET_VIEW_STRING("Target language target", "langrev"); TARGET_VIEW_STRING("Target output name", "name"); TARGET_VIEW_STRING_ARRAY("Warnings used", "warnings", ", "); TARGET_VIEW_STRING_ARRAY("Additional c3l library search paths", "dependency-search-paths", ", "); TARGET_VIEW_STRING_ARRAY("c3l library search paths (override)", "dependency-search-paths-override", ", "); TARGET_VIEW_STRING_ARRAY("Additional c3l library dependencies", "dependencies", ", "); TARGET_VIEW_STRING_ARRAY("c3l library dependencies (override)", "dependencies-override", ", "); TARGET_VIEW_STRING_ARRAY("Additional source paths", "sources", ", "); TARGET_VIEW_STRING_ARRAY("Source paths (override)", "sources-override", ", "); TARGET_VIEW_STRING_ARRAY("Additional C source paths", "c-sources", ", "); TARGET_VIEW_STRING_ARRAY("C source paths (override)", "c-sources-override", ", "); TARGET_VIEW_STRING_ARRAY("Additional C source include directories", "c-include-dirs", ", "); TARGET_VIEW_STRING_ARRAY("C source include directories (override)", "c-include-dirs-override", ", "); TARGET_VIEW_SETTING("Optimization level", "opt", optimization_levels); /* Extended target information */ TARGET_VIEW_STRING("Benchmark function override", "benchfn"); TARGET_VIEW_STRING("C compiler", "cc"); TARGET_VIEW_STRING("Additional C compiler flags", "cflags"); TARGET_VIEW_STRING("C compiler flags (override)", "cflags-override"); TARGET_VIEW_STRING("CPU name", "cpu"); TARGET_VIEW_SETTING("Debug level", "debug-info", debug_levels); TARGET_VIEW_STRING_ARRAY("Additional scripts to run", "exec", ", "); TARGET_VIEW_STRING_ARRAY("Scripts to run (override)", "exec", ", "); TARGET_VIEW_STRING_ARRAY("Enabled features", "features", ", "); TARGET_VIEW_SETTING("Floating point behaviour", "fp-math", fp_math); TARGET_VIEW_STRING_ARRAY("Additional linked libraries", "linked-libraries", ", "); TARGET_VIEW_STRING_ARRAY("Linked libraries (override)", "linked-libraries-override", ", "); TARGET_VIEW_STRING("Linker", "linker"); TARGET_VIEW_STRING_ARRAY("Additional linker search paths", "linker-search-paths", ", "); TARGET_VIEW_STRING_ARRAY("Linker search paths (override)", "linker-search-paths-override", ", "); TARGET_VIEW_STRING_ARRAY("Additional linker arguments", "link-args", ", "); TARGET_VIEW_STRING_ARRAY("Linker arguments (override)", "link-args-override", ", "); TARGET_VIEW_BOOL("Link libc", "link-libc"); TARGET_VIEW_STRING("MacOS SDK directory", "macossdk"); TARGET_VIEW_SETTING("Memory environment", "memory-env", memory_environment); TARGET_VIEW_BOOL("Don't generate/require main function", "no-entry"); TARGET_VIEW_SETTING("Code optimization level", "optlevel", optlevels); TARGET_VIEW_SETTING("Code size optimization", "optsize", optsizes); TARGET_VIEW_STRING("Panic function override", "panicfn"); TARGET_VIEW_BOOL("Panic message output", "panic-msg"); TARGET_VIEW_SETTING("Relocation model", "reloc", reloc_models); TARGET_VIEW_BOOL("Runtime safety checks enabled", "safe"); TARGET_VIEW_BOOL("Print backtrace on signals", "show-backtrace"); TARGET_VIEW_STRING("Script directory", "script-dir"); TARGET_VIEW_STRING("Run directory", "run-dir"); TARGET_VIEW_BOOL("Compile into single module", "single-module"); TARGET_VIEW_BOOL("Output soft-float functions", "soft-float"); TARGET_VIEW_BOOL("Strip unused code/globals", "strip-unused"); TARGET_VIEW_INTEGER("Preferred symtab size", "symtab"); TARGET_VIEW_STRING("Target", "target"); TARGET_VIEW_STRING("Test function override", "testfn"); TARGET_VIEW_BOOL("Integers panic on wrapping", "trap-on-wrap"); TARGET_VIEW_BOOL("Include standard library", "use-stdlib"); TARGET_VIEW_SETTING("Windows CRT linking", "wincrt", wincrt_linking); TARGET_VIEW_STRING("Windows SDK path", "winsdk"); TARGET_VIEW_SETTING("x64 CPU level", "x86cpu", x86_cpu_set); TARGET_VIEW_SETTING("Max vector use type", "x86vec", x86_vector_capability); TARGET_VIEW_BOOL("Return structs on the stack", "x86-stack-struct-return"); TARGET_VIEW_BOOL("Unroll loops", "unroll-loops"); TARGET_VIEW_BOOL("SLP auto-vectorization", "slp-vectorize"); TARGET_VIEW_BOOL("Loop auto-vectorization", "loop-vectorize"); TARGET_VIEW_BOOL("Merge functions", "merge-functions"); } #if FETCH_AVAILABLE void fetch_project(BuildOptions* options) { if (!file_exists(PROJECT_JSON5) && !file_exists(PROJECT_JSON)) { error_exit("Failed: no project file found."); } if (str_eq(options->path, DEFAULT_PATH)) { { const char** deps_dirs = get_project_dependency_directories(); int num_lib = vec_size(deps_dirs); if (num_lib > 0) options->vendor_download_path = deps_dirs[0]; } } const char **libdirs = get_project_dependency_directories(); const char **deps = get_project_dependencies(); const char *filename; JSONObject *project_json = project_json_load(&filename); JSONObject *targets_json = json_map_get(project_json, "targets"); if (targets_json && targets_json->type == J_OBJECT) { FOREACH_IDX(i, JSONObject *, target, targets_json->members) { const char *key = targets_json->keys[i]; if (target->type != J_OBJECT) { error_exit("Invalid data in target '%s'", key); } const char **target_deps = get_optional_string_array((BuildParseContext) { filename, key }, target, "dependencies"); FOREACH(const char*, dep, target_deps) { vec_add(deps, dep); } } } // dependency check tree while (vec_size(deps) > 0) { FOREACH(const char*, dir, libdirs) { const char *dep = VECLAST(deps); if (file_exists(file_append_path(dir, str_printf("%s.c3l", dep)))) { vec_pop(deps); break; } printf("Fetching missing library '%s'...", dep); fflush(stdout); const char *error = vendor_fetch_single(dep, options->vendor_download_path); if (!error) { puts(" finished."); } else { printf("Failed: '%s'\n", error); } vec_pop(deps); break; } } } #else void fetch_project(BuildOptions* options) { error_exit("Error: project fetch only available when compiled with cURL."); } #endif void add_libraries_to_project_file(const char** libs, const char* target_name) { if (!file_exists(PROJECT_JSON5) && !file_exists(PROJECT_JSON)) return; //TODO! Target name option not implemented const char *filename; JSONObject *project_json = project_json_load(&filename); // TODO! check if target is specified and exists (NULL at the moment) JSONObject *libraries_json = json_map_get(project_json, "dependencies"); const char** dependencies = NULL; FOREACH(JSONObject *, element, libraries_json->elements) { vec_add(dependencies, element->str); } // check if libraries are already present FOREACH(const char*, lib, libs) { if (str_findlist(lib, vec_size(dependencies), dependencies)!=-1) continue; vec_add(dependencies, lib); } JSONObject** elements = NULL; FOREACH(const char*, dep, dependencies) { vec_add(elements, json_new_string(dep)); } // Apply changes to JSON object libraries_json->elements = elements; // write to project json file FILE *file = fopen(filename, "w"); if (!file) error_exit("Failed to open file '%s'", filename); print_json_to_file(project_json, file); fclose(file); } void add_target_project(BuildOptions *build_options) { const char *filename; /* NOTE: this previously allowed project.json to not exist, and create it */ JSONObject *project_json = project_json_load(&filename); JSONObject *targets_json = json_map_get(project_json, "targets"); if (targets_json == NULL) { targets_json = json_new_object(J_OBJECT); json_map_set(project_json, "targets", targets_json); } if (json_map_get(targets_json, build_options->project_options.target_name) != NULL) { error_exit("Target with name '%s' already exists", build_options->project_options.target_name); } JSONObject *target_type_obj = json_new_string(targets[build_options->project_options.target_type]); JSONObject *new_target = json_new_map(); json_map_set(new_target, "type", target_type_obj); JSONObject *target_sources = json_new_object(J_ARRAY); FOREACH(const char *, source, build_options->project_options.sources) { vec_add(target_sources->elements, json_new_string(source)); } json_map_set(new_target, "sources", target_sources); json_map_set(targets_json, build_options->project_options.target_name, new_target); FILE *file = fopen(filename, "w"); if (!file) error_exit("Failed to open file '%s'", filename); print_json_to_file(project_json, file); fclose(file); } static void view_filtered_project_properties(BuildOptions *build_options, const char* filename, JSONObject *project_json) { uint16_t bitvector = build_options->project_options.view_modifier.flags_bitvector; bool verbose = build_options->project_options.view_modifier.verbose; BuildParseContext context = { filename, NULL }; const char* delim = verbose ? ", " : "\n"; char* prop_header; if (bitvector & (1 << 0)) { prop_header = verbose ? "Authors" : ""; VIEW_STRING_ARRAY(prop_header, "authors", delim); PRINTFN(""); } if (bitvector & (1 << 1)) { prop_header = verbose ? "Version" : ""; VIEW_STRING(prop_header, "version"); PRINTFN(""); } if (bitvector & (1 << 2)) { prop_header = verbose ? "Project language target" : ""; VIEW_STRING(prop_header, "langrev"); PRINTFN(""); } if (bitvector & (1 << 3)) { prop_header = verbose ? "Warnings used" : ""; VIEW_STRING_ARRAY(prop_header, "warnings", delim); PRINTFN(""); } if (bitvector & (1 << 4)) { prop_header = verbose ? "c3l library search paths" : ""; VIEW_STRING_ARRAY(prop_header, "dependency-search-paths", delim); PRINTFN(""); } if (bitvector & (1 << 5)) { prop_header = verbose ? "c3l library dependencies" : ""; VIEW_STRING_ARRAY(prop_header, "dependencies", delim); PRINTFN(""); } if (bitvector & (1 << 6)) { prop_header = verbose ? "Source paths" : ""; VIEW_STRING_ARRAY(prop_header, "sources", delim); PRINTFN(""); } if (bitvector & (1 << 7)) { prop_header = verbose ? "Output location" : ""; VIEW_STRING(prop_header, "output"); PRINTFN(""); } if (bitvector & (1 << 8)) { prop_header = verbose ? "Default optimization level" : ""; VIEW_SETTING(prop_header, "opt", optimization_levels); PRINTFN(""); } /* Target information */ if (bitvector & (1 << 9)) { if (verbose) PRINTFN("Targets: "); JSONObject *targets_json = json_map_get(project_json, "targets"); if (!targets_json) { error_exit("No targets found in project."); } if (targets_json->type != J_OBJECT) { error_exit("'targets' did not contain map of targets."); } FOREACH_IDX(i, JSONObject *, object, targets_json->members) { const char *key = targets_json->keys[i]; if (object->type != J_OBJECT) { error_exit("Invalid data in target '%s'", key); } view_target((BuildParseContext) { filename, key }, object, verbose); } } } void view_project(BuildOptions *build_options) { const char *filename; JSONObject *project_json = project_json_load(&filename); bool filter_properties = build_options->project_options.view_modifier.flags_bitvector != 0; if (filter_properties) { view_filtered_project_properties(build_options, filename, project_json); return; } BuildParseContext context = { filename, NULL }; /* General information */ VIEW_STRING_ARRAY("Authors", "authors", ", "); VIEW_STRING("Version", "version"); VIEW_STRING("Project language target", "langrev"); VIEW_STRING_ARRAY("Warnings used", "warnings", ", "); VIEW_STRING_ARRAY("c3l library search paths", "dependency-search-paths", ", "); VIEW_STRING_ARRAY("c3l library dependencies", "dependencies", ", "); VIEW_STRING_ARRAY("Source paths", "sources", ", "); VIEW_STRING_ARRAY("C source paths", "c-sources", ", "); VIEW_STRING("Output location", "output"); VIEW_STRING("Build location", "build-dir"); VIEW_STRING("Output extension", "extension"); VIEW_SETTING("Default optimization level", "opt", optimization_levels); /* Extended information */ VIEW_STRING("Benchmark function override", "benchfn"); VIEW_STRING("C compiler", "cc"); VIEW_STRING("C compiler flags", "cflags"); VIEW_STRING("CPU name", "cpu"); VIEW_SETTING("Debug level", "debug-info", debug_levels); VIEW_STRING_ARRAY("Scripts to run", "exec", ", "); VIEW_STRING_ARRAY("Enabled features", "features", ", "); VIEW_SETTING("Floating point behaviour", "fp-math", fp_math); VIEW_STRING_ARRAY("Linked libraries", "linked-libraries", ", "); VIEW_STRING("Linker", "linker"); VIEW_STRING_ARRAY("Linker search paths", "linker-search-paths", ", "); VIEW_STRING_ARRAY("Linker arguments", "link-args", ", "); VIEW_BOOL("Link libc", "link-libc"); VIEW_STRING("MacOS SDK directory", "macossdk"); VIEW_SETTING("Memory environment", "memory-env", memory_environment); VIEW_BOOL("Don't generate/require main function", "no-entry"); VIEW_SETTING("Code optimization level", "optlevel", optlevels); VIEW_SETTING("Code size optimization", "optsize", optsizes); VIEW_STRING("Panic function override", "panicfn"); VIEW_BOOL("Panic message output", "panic-msg"); VIEW_SETTING("Relocation model", "reloc", reloc_models); VIEW_BOOL("Runtime safety checks enabled", "safe"); VIEW_BOOL("Print backtrace on signals", "show-backtrace"); VIEW_STRING("Script directory", "script-dir"); VIEW_STRING("Run directory", "run-dir"); VIEW_BOOL("Compile into single module", "single-module"); VIEW_BOOL("Output soft-float functions", "soft-float"); VIEW_BOOL("Strip unused code/globals", "strip-unused"); VIEW_INTEGER("Preferred symtab size", "symtab"); VIEW_STRING("Target", "target"); VIEW_STRING("Test function override", "testfn"); VIEW_BOOL("Integers panic on wrapping", "trap-on-wrap"); VIEW_BOOL("Include standard library", "use-stdlib"); VIEW_SETTING("Windows CRT linking", "wincrt", wincrt_linking); VIEW_STRING("Windows SDK path", "winsdk"); VIEW_SETTING("x64 CPU level", "x86cpu", x86_cpu_set); VIEW_SETTING("Max vector use type", "x86vec", x86_vector_capability); VIEW_BOOL("Return structs on the stack", "x86-stack-struct-return"); /* Target information */ PRINTFN("Targets: "); JSONObject *targets_json = json_map_get(project_json, "targets"); if (!targets_json) { error_exit("No targets found in project."); } if (targets_json->type != J_OBJECT) { error_exit("'targets' did not contain map of targets."); } FOREACH_IDX(i, JSONObject *, object, targets_json->members) { const char *key = targets_json->keys[i]; if (object->type != J_OBJECT) { error_exit("Invalid data in target '%s'", key); } view_target((BuildParseContext) {filename, key }, object, true); } }