From e748f72447795c5bf61ca8d669973d830ea6e211 Mon Sep 17 00:00:00 2001 From: Lexi Date: Mon, 5 Aug 2024 15:45:15 -0400 Subject: [PATCH] Project view command (#1314) Add parsing for the project command and view subcommand. Add basic implementation of c3c project view. Move get_valid_integer into common build. Co-authored-by: Christoffer Lerno --- CMakeLists.txt | 1 + src/build/build.h | 10 + src/build/build_internal.h | 1 + src/build/build_options.c | 38 ++++ src/build/builder.c | 2 + src/build/common_build.c | 21 ++- src/build/project.c | 18 -- src/build/project_manipulation.c | 314 +++++++++++++++++++++++++++++++ src/main.c | 10 + 9 files changed, 396 insertions(+), 19 deletions(-) create mode 100644 src/build/project_manipulation.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 6944902c3..37066d6d8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -246,6 +246,7 @@ add_executable(c3c src/build/builder.c src/build/build_options.c src/build/project_creation.c + src/build/project_manipulation.c src/build/libraries.c src/compiler/ast.c src/compiler/bigint.c diff --git a/src/build/build.h b/src/build/build.h index af645d566..e32a13e9e 100644 --- a/src/build/build.h +++ b/src/build/build.h @@ -45,8 +45,16 @@ typedef enum COMMAND_TEST, COMMAND_UNIT_TEST, COMMAND_PRINT_SYNTAX, + COMMAND_PROJECT, } CompilerCommand; + +typedef enum +{ + SUBCOMMAND_MISSING = 0, + SUBCOMMAND_VIEW +} ProjectSubcommand; + typedef enum { DIAG_NONE = 0, // Don't use! @@ -388,6 +396,7 @@ typedef struct BuildOptions_ unsigned version; CompilerBackend backend; CompilerCommand command; + ProjectSubcommand subcommand; CompileOption compile_option; TrustLevel trust_level; DiagnosticsSeverity severity[DIAG_END_SENTINEL]; @@ -660,3 +669,4 @@ void update_build_target_with_opt_level(BuildTarget *target, OptimizationSetting void create_project(BuildOptions *build_options); void create_library(BuildOptions *build_options); void resolve_libraries(BuildTarget *build_target); +void view_project(BuildOptions *build_options); \ No newline at end of file diff --git a/src/build/build_internal.h b/src/build/build_internal.h index 1853dbc83..a444e1e2b 100644 --- a/src/build/build_internal.h +++ b/src/build/build_internal.h @@ -110,3 +110,4 @@ void get_list_append_strings(const char *file, const char *target, JSONObject *j const char *base, const char *override, const char *add); int get_valid_string_setting(const char *file, const char *target, JSONObject *json, const char *key, const char** values, int first_result, int count, const char *expected); void check_json_keys(const char* valid_keys[][2], size_t key_count, const char* deprecated_keys[], size_t deprecated_key_count, JSONObject *json, const char *target_name, const char *option); +long get_valid_integer(JSONObject *table, const char *key, const char *category, bool mandatory); diff --git a/src/build/build_options.c b/src/build/build_options.c index c50ef6dda..b8600e9c0 100644 --- a/src/build/build_options.c +++ b/src/build/build_options.c @@ -51,6 +51,7 @@ const char *trust_level[3] = { #define EOUTPUT(string, ...) fprintf(stderr, string "\n", ##__VA_ARGS__) // NOLINT #define PRINTF(string, ...) fprintf(stdout, string "\n", ##__VA_ARGS__) // NOLINT #define FAIL_WITH_ERR(string, ...) do { fprintf(stderr, "Error: " string "\n\n", ##__VA_ARGS__); usage(); exit_compiler(EXIT_FAILURE); } while (0) /* NOLINT */ +#define PROJECT_FAIL_WITH_ERR(string, ...) do { fprintf(stderr, "Error: " string "\n\n", ##__VA_ARGS__); project_usage(); exit_compiler(EXIT_FAILURE); } while (0) /* NOLINT */ static void usage(void) { @@ -78,6 +79,7 @@ static void usage(void) PRINTF(" dynamic-lib [ ...] Compile files without a project into a dynamic library."); PRINTF(" headers [ ...] Analyse files and generate C headers for public methods."); PRINTF(" vendor-fetch ... Fetches one or more libraries from the vendor collection."); + PRINTF(" project ... Manipulate or view project files."); PRINTF(""); PRINTF("Options:"); PRINTF(" --tb - Use Tilde Backend for compilation."); @@ -277,6 +279,35 @@ static void parse_optional_target(BuildOptions *options) } } +static void project_usage() +{ + PRINTF("Usage: %s [] project []", args[0]); + PRINTF(""); + PRINTF("Project Subcommands:"); + PRINTF(" view view the current projects structure"); +} + +static void parse_project_subcommand(BuildOptions *options) +{ + if (arg_match("view")) + { + options->subcommand = SUBCOMMAND_VIEW; + return; + } + PROJECT_FAIL_WITH_ERR("Cannot process the unknown subcommand \"%s\".", current_arg); +} + +static void parse_project_options(BuildOptions *options) +{ + if (at_end()) + { + project_usage(); + return; + } + next_arg(); + parse_project_subcommand(options); +} + static void parse_command(BuildOptions *options) { if (arg_match("init")) @@ -404,6 +435,13 @@ static void parse_command(BuildOptions *options) parse_optional_target(options); return; } + if (arg_match("project")) + { + options->command = COMMAND_PROJECT; + options->subcommand = SUBCOMMAND_MISSING; + parse_project_options(options); + return; + } FAIL_WITH_ERR("Cannot process the unknown command \"%s\".", current_arg); } static void print_all_targets(void) diff --git a/src/build/builder.c b/src/build/builder.c index f01877a9d..babf7a980 100644 --- a/src/build/builder.c +++ b/src/build/builder.c @@ -87,6 +87,7 @@ bool command_accepts_files(CompilerCommand command) case COMMAND_BENCHMARK: case COMMAND_TEST: case COMMAND_VENDOR_FETCH: + case COMMAND_PROJECT: return false; } UNREACHABLE @@ -120,6 +121,7 @@ bool command_passes_args(CompilerCommand command) case COMMAND_BENCHMARK: case COMMAND_TEST: case COMMAND_VENDOR_FETCH: + case COMMAND_PROJECT: return false; } UNREACHABLE diff --git a/src/build/common_build.c b/src/build/common_build.c index 64bd60c51..656abd69d 100644 --- a/src/build/common_build.c +++ b/src/build/common_build.c @@ -1,5 +1,6 @@ #include "build_internal.h" #include "utils/common.h" +#include void check_json_keys(const char* valid_keys[][2], size_t key_count, const char* deprecated_keys[], size_t deprecated_key_count, JSONObject *json, const char *target_name, const char *option) { @@ -49,7 +50,7 @@ const char *get_mandatory_string(const char *file, const char *category, JSONObj if (!value) { if (category) error_exit("In file '%s': The mandatory field '%s' was missing in '%s'.", file, key, category); - error_exit("In file '%s': The mandatory field '%s' was missing.", file); + error_exit("In file '%s': The mandatory field '%s' was missing.", file, key); } return value; } @@ -176,3 +177,21 @@ int get_valid_string_setting(const char *file, const char *target, JSONObject *j } error_exit("In file '%s': Invalid value for '%s', expected %s", file, key, expected); } + +long get_valid_integer(JSONObject *table, const char *key, const char *category, bool mandatory) +{ + JSONObject *value = json_obj_get(table, key); + if (!value) + { + if (mandatory) + { + error_exit("%s was missing a mandatory '%s' field, please add it.", category, key); + } + return -1; + } + if (value->type != J_NUMBER || trunc(value->f) != value->f) + { + error_exit("%s had an invalid mandatory '%s' field that was not an integer, please correct it.", category, key); + } + return (long)trunc(value->f); +} \ No newline at end of file diff --git a/src/build/project.c b/src/build/project.c index cceac7d30..d1fcaa9f8 100644 --- a/src/build/project.c +++ b/src/build/project.c @@ -133,24 +133,6 @@ const int project_target_keys_count = ELEMENTLEN(project_target_keys); const int project_deprecated_target_keys_count = ELEMENTLEN(project_deprecated_target_keys); -long get_valid_integer(JSONObject *table, const char *key, const char *category, bool mandatory) -{ - JSONObject *value = json_obj_get(table, key); - if (!value) - { - if (mandatory) - { - error_exit("%s was missing a mandatory '%s' field, please add it.", category, key); - } - return -1; - } - if (value->type != J_NUMBER || trunc(value->f) != value->f) - { - error_exit("%s had an invalid mandatory '%s' field that was not an integer, please correct it.", category, key); - } - return (long)trunc(value->f); -} - static void load_into_build_target(JSONObject *json, const char *target_name, BuildTarget *target) { diff --git a/src/build/project_manipulation.c b/src/build/project_manipulation.c new file mode 100644 index 000000000..5f2e44947 --- /dev/null +++ b/src/build/project_manipulation.c @@ -0,0 +1,314 @@ +#include "build_internal.h" +#define PRINTFN(string, ...) fprintf(stdout, string "\n", ##__VA_ARGS__) // NOLINT +#define PRINTF(string, ...) fprintf(stdout, string, ##__VA_ARGS__) // NOLINT +static JSONObject* read_project() +{ + size_t size; + char *read = file_read_all(PROJECT_JSON, &size); + JsonParser parser; + json_init_string(&parser, read, &malloc_arena); + JSONObject *json = json_parse(&parser); + if (parser.error_message) + { + error_exit("Error on line %d reading '%s':'%s'", parser.line, PROJECT_JSON, parser.error_message); + } + if (!json || json->type != J_OBJECT) + { + error_exit("Expected a map of project information in '%s'.", PROJECT_JSON); + } + return json; +} + +static void print_vec(const char* header, const char** vec, bool opt) +{ + if (opt && !vec) return; + PRINTF("%s: ", header); + if (!vec_size(vec)) + { + PRINTFN("*none*"); + return; + } + FOREACH_IDX(i, const char *, item, vec) + { + if (i > 0) PRINTF(", "); + PRINTF("%s",item); + } + PRINTFN(""); +} + +static void print_opt_str(const char* header, const char* str) +{ + if (!str) return; + PRINTFN("%s: %s", header, str); +} + +static void print_opt_setting(const char* header, int setting, const char** values) +{ + if (setting < 0) return; + PRINTFN("%s: %s", header, values[setting]); +} + +static void print_opt_bool(const char* header, int b) +{ + if (b == -1) return; + PRINTF("%s: ", header); + PRINTFN(b ? "true" : "false"); +} + +static void print_opt_int(const char* header, long v) +{ + if (v < 0) return; + PRINTFN("%s: %ld", header, 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) \ +do { \ + const char** arr = get_string_array(PROJECT_JSON, NULL, project_json, key, true);\ + print_vec(header, arr, false);\ +} while(0) + +#define VIEW_STRING_ARRAY(header, key) \ +do { \ + const char** arr = get_optional_string_array(PROJECT_JSON, NULL, project_json, key);\ + print_vec(header, arr, true);\ +} while(0) + +#define VIEW_MANDATORY_STRING(header, key) \ +do { \ + const char* str = get_mandatory_string(PROJECT_JSON, NULL, project_json, key);\ + print_opt_str(header, str);\ +} while(0) + +#define VIEW_STRING(header, key) \ +do { \ + const char* str = get_optional_string(PROJECT_JSON, NULL, project_json, key);\ + print_opt_str(header, str);\ +} while(0) + +#define VIEW_SETTING(header, key, expected_arr) \ +do { \ + int setting = get_valid_string_setting(PROJECT_JSON, NULL, 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(PROJECT_JSON, NULL, project_json, key, -1);\ + print_opt_bool(header, val);\ +} while(0) + +#define VIEW_INTEGER(header, key) \ +do {\ + long v = get_valid_integer(project_json, key, PROJECT_JSON, false);\ + print_opt_int(header, v);\ +} while(0); + +#define TARGET_VIEW_STRING_ARRAY(header, key) \ +do {\ + const char** arr = get_optional_string_array(PROJECT_JSON, name, target, key);\ + print_vec("\t" header, arr, true);\ +} while(0) + +#define TARGET_VIEW_MANDATORY_STRING(header, key) \ +do { \ + const char* str = get_mandatory_string(PROJECT_JSON, name, target, key);\ + print_opt_str("\t" header, str);\ +} while(0) + +#define TARGET_VIEW_STRING(header, key) \ +do { \ + const char* str = get_optional_string(PROJECT_JSON, name, 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(PROJECT_JSON, name, 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(PROJECT_JSON, name, target, key, -1);\ + print_opt_bool("\t" header, val);\ +} while(0) + +#define TARGET_VIEW_INTEGER(header, key) \ +do {\ + long v = get_valid_integer(target, key, name, false);\ + print_opt_int("\t" header, v);\ +} while(0); + +static void view_target(const char* name, JSONObject* target) +{ + /* General target information */ + PRINTFN("- %s",name); + print_opt_str("\tName", name); + TARGET_VIEW_MANDATORY_STRING("Type","type"); + TARGET_VIEW_STRING("Target language target", "langrev"); + 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_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_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"); +} + +void view_project(BuildOptions *build_options) +{ + JSONObject* project_json = read_project(); + + /* General information */ + VIEW_MANDATORY_STRING_ARRAY("Authors","authors"); + VIEW_MANDATORY_STRING("Version","version"); + VIEW_MANDATORY_STRING("Project language target", "langrev"); + VIEW_MANDATORY_STRING_ARRAY("Warnings used", "warnings"); + VIEW_MANDATORY_STRING_ARRAY("c3l library search paths", "dependency-search-paths"); + VIEW_MANDATORY_STRING_ARRAY("c3l library dependencies", "dependencies"); + VIEW_MANDATORY_STRING_ARRAY("Source paths", "sources"); + VIEW_STRING_ARRAY("C source paths", "c-sources"); + VIEW_STRING("Output location", "output"); + 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_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_obj_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."); + } + + for (unsigned i = 0; i < targets_json->member_len; i++) + { + JSONObject *object = targets_json->members[i]; + const char *key = targets_json->keys[i]; + if (object->type != J_OBJECT) + { + error_exit("Invalid data in target '%s'", key); + } + view_target(key,object); + } +} diff --git a/src/main.c b/src/main.c index 89edb9335..d1514f04b 100644 --- a/src/main.c +++ b/src/main.c @@ -98,6 +98,16 @@ int main_real(int argc, const char *argv[]) case COMMAND_TEST: compile_file_list(&build_options); break; + case COMMAND_PROJECT: + switch (build_options.subcommand) + { + case SUBCOMMAND_VIEW: + view_project(&build_options); + break; + case SUBCOMMAND_MISSING: + UNREACHABLE + } + break; case COMMAND_MISSING: UNREACHABLE }