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 <christoffer.lerno@gmail.com>
This commit is contained in:
Lexi
2024-08-05 15:45:15 -04:00
committed by GitHub
parent 14358417c8
commit e748f72447
9 changed files with 396 additions and 19 deletions

View File

@@ -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

View File

@@ -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);

View File

@@ -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);

View File

@@ -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 <file1> [<file2> ...] Compile files without a project into a dynamic library.");
PRINTF(" headers <file1> [<file2> ...] Analyse files and generate C headers for public methods.");
PRINTF(" vendor-fetch <library> ... Fetches one or more libraries from the vendor collection.");
PRINTF(" project <subcommand> ... 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 [<options>] project <subcommand> [<args>]", 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)

View File

@@ -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

View File

@@ -1,5 +1,6 @@
#include "build_internal.h"
#include "utils/common.h"
#include <math.h>
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);
}

View File

@@ -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)
{

View File

@@ -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);
}
}

View File

@@ -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
}