diff --git a/resources/project_schema.json b/resources/project_schema.json index f2a26e68c4..e8f07c697c 100644 --- a/resources/project_schema.json +++ b/resources/project_schema.json @@ -75,7 +75,21 @@ "type": { "type": "string", "description": "Executable or library type for the target.", - "enum": ["executable", "static-lib", "dynamic-lib"] + "enum": ["executable", "static-lib", "dynamic-lib", "benchmark", "test", "object-files", "prepare"] + }, + "name": { + "type": "string", + "description": "Override the output name for this target." + }, + "extension": { + "type": "string", + "description": "Override the default file extension for the build output. Must start with '.'.", + "pattern": "^\\..*" + }, + "preset": { + "type": "string", + "description": "Reference a preset configuration from a library dependency, format: 'library:preset'.", + "pattern": "^[a-z][a-z0-9_]{0,126}:[a-z][a-z0-9_]{0,126}$" }, "dependencies": { "type": "array", @@ -97,6 +111,66 @@ "items": { "type": "string" } + }, + "features": { + "type": "array", + "description": "Feature flags enabled for this target.", + "items": { + "type": "string" + } + }, + "opt": { + "type": "string", + "description": "Optimization setting.", + "enum": ["O0", "O1", "O2", "O3", "O4", "O5", "Os", "Oz"] + }, + "cpu": { + "type": "string", + "description": "CPU name, used for optimizations in the LLVM backend." + }, + "cpu-flags": { + "type": "string", + "description": "Additional CPU flags to add or remove with the format '+avx,-sse'." + }, + "target": { + "type": "string", + "description": "Compile for a particular architecture + OS target." + }, + "linker": { + "type": "string", + "description": "Use 'builtin' for the builtin linker, 'cc' for the system linker or a path to a custom compiler." + }, + "riscv-cpu": { + "type": "string", + "description": "Set general level of RISC-V CPU.", + "enum": ["rvi", "rvimac", "rvimafc", "rvgc", "rvgcv"] + }, + "riscv-abi": { + "type": "string", + "description": "RISC-V ABI.", + "enum": ["int-only", "float", "double"] + }, + "memory-env": { + "type": "string", + "description": "Memory environment.", + "enum": ["normal", "small", "tiny", "none"] + }, + "reloc": { + "type": "string", + "description": "Relocation model.", + "enum": ["none", "pic", "PIC", "pie", "PIE"] + }, + "use-stdlib": { + "type": "boolean", + "description": "Include the standard library." + }, + "link-libc": { + "type": "boolean", + "description": "Link libc other default libraries." + }, + "no-entry": { + "type": "boolean", + "description": "Do not generate or require a main function." } }, "required": ["type"] @@ -112,6 +186,20 @@ "description": "CPU name, used for optimizations in the LLVM backend.", "default": "generic" }, + "cpu-flags": { + "type": "string", + "description": "Additional CPU flags to add or remove with the format '+avx,-sse'." + }, + "riscv-cpu": { + "type": "string", + "description": "Set general level of RISC-V CPU.", + "enum": ["rvi", "rvimac", "rvimafc", "rvgc", "rvgcv"] + }, + "riscv-abi": { + "type": "string", + "description": "RISC-V ABI.", + "enum": ["int-only", "float", "double"] + }, "debug-info": { "type": "string", "description": "Debug information level.", @@ -194,6 +282,11 @@ "description": "Use the system linker.", "default": "cc" }, + "no-entry": { + "type": "boolean", + "description": "Do not generate or require a main function.", + "default": false + }, "use-stdlib": { "type": "boolean", "description": "Include the standard library.", diff --git a/resources/testproject/lib/clib3.c3l/clib3.c3i b/resources/testproject/lib/clib3.c3l/clib3.c3i new file mode 100644 index 0000000000..752214c12c --- /dev/null +++ b/resources/testproject/lib/clib3.c3l/clib3.c3i @@ -0,0 +1,4 @@ +module clib3; + +extern fn void hello_from_preset_feature(); +extern fn void hello_from_local_feature(); diff --git a/resources/testproject/lib/clib3.c3l/feature.c b/resources/testproject/lib/clib3.c3l/feature.c new file mode 100644 index 0000000000..471902d747 --- /dev/null +++ b/resources/testproject/lib/clib3.c3l/feature.c @@ -0,0 +1,6 @@ +#include + +void hello_from_preset_feature(void) +{ + printf("Hello from clib3 preset feature!\n"); +} diff --git a/resources/testproject/lib/clib3.c3l/local_feature.c b/resources/testproject/lib/clib3.c3l/local_feature.c new file mode 100644 index 0000000000..070629fc4b --- /dev/null +++ b/resources/testproject/lib/clib3.c3l/local_feature.c @@ -0,0 +1,6 @@ +#include + +void hello_from_local_feature(void) +{ + printf("Hello from clib3 local feature!\n"); +} diff --git a/resources/testproject/lib/clib3.c3l/manifest.json b/resources/testproject/lib/clib3.c3l/manifest.json new file mode 100644 index 0000000000..bf04739106 --- /dev/null +++ b/resources/testproject/lib/clib3.c3l/manifest.json @@ -0,0 +1,30 @@ +{ + "provides": "clib3", + "presets": { + "native_feature": { + "features": ["CLIB3_PRESET_TEST"] + }, + "native_local_feature": { + "features": ["CLIB3_LOCAL_TEST"] + } + }, + "features": { + "CLIB3_PRESET_TEST": { + "c-sources": ["feature.c"] + }, + "CLIB3_LOCAL_TEST": { + "c-sources": ["local_feature.c"] + } + }, + "targets": { + "macos-x64": {}, + "macos-aarch64": {}, + "netbsd-x64": {}, + "netbsd-aarch64": {}, + "openbsd-x64": {}, + "linux-x64": {}, + "windows-x64": {}, + "android-aarch64": {}, + "android-x86_64": {} + } +} diff --git a/resources/testproject/project.json b/resources/testproject/project.json index 147322786d..f756755e26 100644 --- a/resources/testproject/project.json +++ b/resources/testproject/project.json @@ -12,7 +12,7 @@ "src" ], "dependency-search-paths": [ "./lib" ], - "dependencies": ["clib", "clib2"], + "dependencies": ["clib", "clib2", "clib3"], "cpu-flags": ",,", "features": ["ABCD"], "exec": ["scriptme.c3 myarg"], @@ -26,6 +26,33 @@ ], "reloc": "PIE", }, + "hello_world_preset_feature": { + "type": "executable", + "preset": "clib3:native_feature", + "cc" : "cc", + "c-sources": [ + "./csource/**" + ], + "reloc": "PIE", + }, + "hello_world_library_feature": { + "type": "executable", + "features": ["CLIB3_LOCAL_TEST"], + "cc" : "cc", + "c-sources": [ + "./csource/**" + ], + "reloc": "PIE", + }, + "hello_world_preset_local_feature": { + "type": "executable", + "preset": "clib3:native_local_feature", + "cc" : "cc", + "c-sources": [ + "./csource/**" + ], + "reloc": "PIE", + }, "hello_world_win32": { "type": "executable", "c-include-dirs": [ "C:\\"], diff --git a/resources/testproject/src/hello/hello_world.c3 b/resources/testproject/src/hello/hello_world.c3 index f4bb75f378..8b28234cdf 100644 --- a/resources/testproject/src/hello/hello_world.c3 +++ b/resources/testproject/src/hello/hello_world.c3 @@ -1,5 +1,5 @@ module hello_world; -import std, bar, clib, clib2; +import std, bar, clib, clib2, clib3; fn int test_doubler(int x) @if(env::WIN32) => x * x; extern fn int test_doubler(int) @if(!env::WIN32); @@ -23,5 +23,7 @@ fn int main() if ($feature(ABCD)) io::printn("ABCD"); clib::hello_from_c(); clib2::hello_from_c_zip(); + if ($feature(CLIB3_PRESET_TEST)) clib3::hello_from_preset_feature(); + if ($feature(CLIB3_LOCAL_TEST)) clib3::hello_from_local_feature(); return 0; } \ No newline at end of file diff --git a/scripts/tools/ci_tests.sh b/scripts/tools/ci_tests.sh index e912fec040..3abc963aed 100755 --- a/scripts/tools/ci_tests.sh +++ b/scripts/tools/ci_tests.sh @@ -211,6 +211,18 @@ run_testproject() { run_c3c run -vv $ARGS run_c3c clean + echo "Running Test Project (library preset feature)..." + run_c3c -vv run hello_world_preset_feature $ARGS + run_c3c clean + + echo "Running Test Project (library feature)..." + run_c3c -vv run hello_world_library_feature $ARGS + run_c3c clean + + echo "Running Test Project (library preset with alternate feature)..." + run_c3c -vv run hello_world_preset_local_feature $ARGS + run_c3c clean + if [[ "$OS_MODE" == "windows" ]]; then echo "Running Test Project (hello_world_win32)..." run_c3c -vv --emit-llvm run hello_world_win32 $ARGS diff --git a/src/build/build.h b/src/build/build.h index 8a83377a35..84584c6a0c 100644 --- a/src/build/build.h +++ b/src/build/build.h @@ -18,6 +18,7 @@ #define DEFAULT_SWITCH_JUMP_MAX_SIZE (0x3FFF) #define DEFAULT_PATH "." +typedef struct JSONObject_ JSONObject; typedef enum { @@ -358,6 +359,7 @@ typedef struct Library__ const char **csource_dirs; const char **cinclude_dirs; WinCrtLinking win_crt; + JSONObject *features; LibraryTarget *target_used; LibraryTarget **targets; } Library; diff --git a/src/build/build_internal.h b/src/build/build_internal.h index 0f1ca4c261..ac987c526e 100644 --- a/src/build/build_internal.h +++ b/src/build/build_internal.h @@ -11,6 +11,7 @@ typedef struct { const char *file; const char *target; + bool is_preset; } BuildParseContext; typedef struct @@ -165,6 +166,7 @@ static const char *sanitize_modes[4] = { JSONObject *project_json_load(const char **filename_ref); Project *project_load(const char **filename_ref); BuildTarget *project_select_target(const char *filename, Project *project, const char *optional_target); +JSONObject *read_library_manifest_for_path(const char *lib_path, const char **manifest_path_ref); const char *get_string(BuildParseContext context, JSONObject *table, const char *key, const char *default_value); int get_valid_bool(BuildParseContext context, JSONObject *json, const char *key, int default_val); diff --git a/src/build/common_build.c b/src/build/common_build.c index b049db34f1..b568c51313 100644 --- a/src/build/common_build.c +++ b/src/build/common_build.c @@ -22,6 +22,10 @@ void check_json_keys(const char* valid_keys[][2], size_t key_count, const char* goto OK; } } + if (target_name && strncmp(target_name, "preset '", strlen("preset '")) == 0) + { + error_exit("Unknown parameter '%s' in '%s'. You can use '%s' to list all valid properties.", key, target_name, option); + } WARNING("Unknown parameter '%s' in '%s'", key, target_name); failed = true; OK:; diff --git a/src/build/libraries.c b/src/build/libraries.c index 537d03e55d..67e7ae6751 100644 --- a/src/build/libraries.c +++ b/src/build/libraries.c @@ -11,7 +11,9 @@ const char *manifest_default_keys[][2] = { {"cflags", "C compiler flags."}, {"dependencies", "List of C3 libraries to also include."}, {"exec", "Scripts run for all platforms."}, + {"features", "Optional feature-specific library configuration, keyed by feature name."}, {"linklib-dir", "Set the directory where to find linked libraries."}, + {"presets", "Predefined target configurations for users of this library."}, {"provides", "The library name"}, {"targets", "The map of supported platforms"}, {"vendor", "Vendor specific extensions, ignored by c3c."}, @@ -32,6 +34,7 @@ const char *manifest_target_keys[][2] = { {"cflags-override", "C compiler flags for the target, overriding global settings."}, {"dependencies", "List of C3 libraries to also include for this target."}, {"exec", "Scripts to also run for the target."}, + {"features", "Optional feature-specific library configuration, keyed by feature name."}, {"linked-libraries", "Libraries linked by the linker for this target, overriding global settings."}, {"link-args", "Linker arguments for this target."}, {"vendor", "Vendor specific extensions, ignored by c3c."}, @@ -120,6 +123,11 @@ static Library *add_library(JSONObject *json, const char *dir, const char **libs library->cc = get_optional_string(context, json, "cc"); library->cflags = get_cflags(context, json, NULL); library->win_crt = (WinCrtLinking)get_valid_string_setting(context, json, "wincrt", wincrt_linking, 0, 3, "'none', 'static' or 'dynamic'."); + library->features = json_map_get(json, "features"); + if (library->features && library->features->type != J_OBJECT) + { + error_exit("Invalid 'features' in %s, expected a map of feature names to library settings.", library->dir); + } APPEND_STRING_LIST(&library->source_dirs, "sources"); APPEND_STRING_LIST(&library->csource_dirs, "c-sources"); APPEND_STRING_LIST(&library->cinclude_dirs, "c-include-dirs"); @@ -137,6 +145,35 @@ static Library *find_library(Library **libs, size_t lib_count, const char *name) UNREACHABLE } +static bool build_target_has_feature(BuildTarget *build_target, const char *feature) +{ + FOREACH(const char *, enabled_feature, build_target->feature_list) + { + if (str_eq(enabled_feature, feature)) return true; + } + return false; +} + +static void apply_library_features(BuildTarget *build_target, Library *library, LibraryTarget *target) +{ + if (!library->features) return; + FOREACH_IDX(i, JSONObject *, feature_json, library->features->members) + { + const char *feature = library->features->keys[i]; + if (!str_is_valid_constant(feature)) + { + error_exit("Invalid feature name '%s' in %s, expected an all-uppercase constant name.", feature, library->dir); + } + if (!build_target_has_feature(build_target, feature)) continue; + if (feature_json->type != J_OBJECT) + { + error_exit("Invalid definition for feature '%s' in %s, expected a map of library settings.", feature, library->dir); + } + check_json_keys(manifest_target_keys, manifest_target_keys_count, manifest_deprecated_target_keys, manifest_deprecated_target_key_count, feature_json, feature, "--list-manifest-properties"); + parse_library_target(library, target, feature, feature_json); + } +} + static void add_library_dependency(BuildTarget *build_target, Library *library, Library **library_list, size_t lib_count) { if (library->target_used) return; @@ -154,6 +191,7 @@ static void add_library_dependency(BuildTarget *build_target, Library *library, error_exit("Library '%s' cannot be used with arch/os '%s'.", library->provides, arch_os_target[build_target->arch_os_target]); } library->target_used = target_found; + apply_library_features(build_target, library, target_found); FOREACH(const char *, dependency, library->dependencies) { add_library_dependency(build_target, find_library(library_list, lib_count, dependency), library_list, lib_count); @@ -179,7 +217,7 @@ INLINE JSONObject* read_manifest(const char *lib, const char *manifest_data) JSONObject *json = json_parse(&parser); if (parser.error_message) { - error_exit("Error on line %d reading '%s':'%s'", parser.line, lib, parser.error_message); + error_exit("Error on line %d reading '%s':%s", parser.line, lib, parser.error_message); } if (!json) { @@ -188,6 +226,47 @@ INLINE JSONObject* read_manifest(const char *lib, const char *manifest_data) return json; } +JSONObject *read_library_manifest_for_path(const char *lib_path, const char **manifest_path_ref) +{ + if (file_is_dir(lib_path)) + { + const char *manifest_path = file_append_path(lib_path, MANIFEST_FILE); + if (!file_exists(manifest_path)) return NULL; + size_t size; + char *manifest_data = file_read_all(manifest_path, &size); + *manifest_path_ref = manifest_path; + return read_manifest(manifest_path, manifest_data); + } + if (!file_exists(lib_path)) return NULL; + + FILE *f = fopen(lib_path, "rb"); + if (!f) return NULL; + + ZipDirIterator iterator; + const char *zip_error = zip_dir_iterator(f, &iterator); + if (zip_error) + { + fclose(f); + zip_check_err(lib_path, zip_error); + } + + ZipFile file; + while (iterator.current_file < iterator.files) + { + zip_check_err(lib_path, zip_dir_iterator_next(&iterator, &file)); + if (strcmp(file.name, MANIFEST_FILE) == 0) + { + char *manifest_data; + zip_check_err(lib_path, zip_file_read(f, &file, (void**)&manifest_data)); + fclose(f); + *manifest_path_ref = lib_path; + return read_manifest(lib_path, manifest_data); + } + } + fclose(f); + return NULL; +} + static inline JSONObject *resolve_zip_library(BuildTarget *build_target, const char *lib, const char **resulting_library) { FILE *f = fopen(lib, "rb"); diff --git a/src/build/project.c b/src/build/project.c index 73d4062c08..9032daf0fc 100644 --- a/src/build/project.c +++ b/src/build/project.c @@ -138,6 +138,7 @@ const char* project_target_keys[][2] = { {"output", "Output location, relative to project file."}, {"panic-msg", "Turn panic message output on or off."}, {"panicfn", "Override the panic function."}, + {"preset", "Use a preset configuration from a library, format 'library:preset'."}, {"quiet", "Silence unnecessary output."}, {"reloc", "Relocation model: none, pic, PIC, pie, PIE."}, {"riscv-abi", "RiscV ABI: int-only, float, double."}, @@ -183,9 +184,10 @@ const int project_deprecated_target_keys_count = ELEMENTLEN(project_deprecated_t // Json -> target / default target static void load_into_build_target(BuildParseContext context, JSONObject *json, BuildTarget *target) { - if (context.target) + if (context.target || context.is_preset) { - check_json_keys(project_target_keys, project_target_keys_count, project_deprecated_target_keys, project_deprecated_target_keys_count, json, context.target, "--list-project-properties"); + check_json_keys(project_target_keys, project_target_keys_count, project_deprecated_target_keys, project_deprecated_target_keys_count, + json, context.target ? context.target : "preset", "--list-project-properties"); } else { @@ -357,7 +359,8 @@ static void load_into_build_target(BuildParseContext context, JSONObject *json, target->feature.panic_level); // Overridden name - target->output_name = get_optional_string(context, json, "name"); + const char* name = get_optional_string(context, json, "name"); + if (name) target->output_name = name; // Single module target->single_module = (SingleModule) get_valid_bool(context, json, "single-module", target->single_module); @@ -588,6 +591,100 @@ static void duplicate_prop(const char ***prop_ref) } *prop_ref = copy; } + +static void parse_preset_ref(const char *ref, const char **lib_name, const char **preset_name) +{ + const char *colon = strchr(ref, ':'); + if (!colon || colon == ref || colon[1] == '\0' || strchr(colon + 1, ':')) + { + error_exit("Error reading project: invalid preset reference '%s', expected 'library:preset'.", ref); + } + *lib_name = str_copy(ref, colon - ref); + *preset_name = str_copy(colon + 1, strlen(colon + 1)); + if (!str_is_valid_lowercase_name(*lib_name)) + { + error_exit("Error reading project: invalid library name '%s' in preset reference '%s' - it should only contain alphanumerical letters and '_'.", *lib_name, ref); + } + if (!str_is_valid_lowercase_name(*preset_name)) + { + error_exit("Error reading project: invalid preset name '%s' in preset reference '%s' - it should only contain alphanumerical letters and '_'.", *preset_name, ref); + } +} + +static JSONObject* resolve_preset(BuildTarget *target, const char *preset_ref, const char **manifest_path_ref) +{ + const char *lib_name; + const char *preset_name; + parse_preset_ref(preset_ref, &lib_name, &preset_name); + + bool found_dep = false; + FOREACH(const char *, dep, target->libs) + { + if (str_eq(dep, lib_name)) + { + found_dep = true; + break; + } + } + if (!found_dep) + { + error_exit("Error reading project: preset '%s' references library '%s' which is not listed in 'dependencies'.", preset_ref, lib_name); + } + + JSONObject *manifest = NULL; + static const char *c3lib_suffix = ".c3l"; + const char **c3_libs = NULL; + if (vec_size(target->libdirs)) + { + FOREACH(const char *, dir, target->libdirs) + { + file_add_wildcard_files(&c3_libs, dir, false, &c3lib_suffix, 1); + } + } + else + { + file_add_wildcard_files(&c3_libs, ".", false, &c3lib_suffix, 1); + } + + FOREACH(const char *, lib_path, c3_libs) + { + const char *manifest_path = NULL; + JSONObject *candidate = read_library_manifest_for_path(lib_path, &manifest_path); + if (!candidate) continue; + BuildParseContext manifest_context = { manifest_path, NULL }; + const char *provides = get_optional_string(manifest_context, candidate, "provides"); + if (provides && str_eq(provides, lib_name)) + { + manifest = candidate; + *manifest_path_ref = manifest_path; + break; + } + } + + if (!manifest) + { + error_exit("Error reading project: could not find manifest for library '%s' needed by preset '%s'.", lib_name, preset_ref); + } + + JSONObject *presets = json_map_get(manifest, "presets"); + if (!presets || presets->type != J_OBJECT) + { + error_exit("Error reading %s: library '%s' does not define any presets.", *manifest_path_ref, lib_name); + } + + JSONObject *preset = json_map_get(presets, preset_name); + if (!preset) + { + error_exit("Error reading %s: preset '%s' not found in library '%s'.", *manifest_path_ref, preset_name, lib_name); + } + if (preset->type != J_OBJECT) + { + error_exit("Error reading %s: preset '%s' in library '%s' is not a JSON object.", *manifest_path_ref, preset_name, lib_name); + } + + return preset; +} + static void project_add_target(BuildParseContext context, Project *project, BuildTarget *default_target, JSONObject *json, const char *type, TargetType target_type) { @@ -609,6 +706,24 @@ static void project_add_target(BuildParseContext context, Project *project, Buil duplicate_prop(&target->linker_libs); duplicate_prop(&target->link_args); + BuildParseContext target_context = { context.file, str_printf("%s %s", type, context.target) }; + const char *preset_ref = get_optional_string(target_context, json, "preset"); + if (preset_ref) + { + // Target-local dependencies and dependency-search-paths are needed to find the preset. + // Other target-local settings are intentionally loaded after the preset so they override it. + const char **default_libdirs = target->libdirs; + const char **default_libs = target->libs; + APPEND_STRING_LIST(&target->libdirs, "dependency-search-paths"); + APPEND_STRING_LIST(&target->libs, "dependencies"); + const char *manifest_path = NULL; + JSONObject *preset_json = resolve_preset(target, preset_ref, &manifest_path); + target->libdirs = default_libdirs; + target->libs = default_libs; + BuildParseContext preset_context = { manifest_path, str_printf("preset '%s'", preset_ref), true }; + load_into_build_target(preset_context, preset_json, target); + } + vec_add(project->targets, target); target->name = context.target; target->type = target_type; @@ -620,8 +735,7 @@ static void project_add_target(BuildParseContext context, Project *project, Buil error_exit("More %s contained more than one target with the name %s. Please make all target names unique.", context.file, target->name); } } - context.target = str_printf("%s %s", type, context.target); - load_into_build_target(context, json, target); + load_into_build_target(target_context, json, target); } static void project_add_targets(const char *filename, Project *project, JSONObject *project_data) diff --git a/src/build/project_manipulation.c b/src/build/project_manipulation.c index 5dab9205df..c45cd2f87d 100644 --- a/src/build/project_manipulation.c +++ b/src/build/project_manipulation.c @@ -226,6 +226,8 @@ static void view_target(BuildParseContext context, JSONObject *target, bool verb 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_STRING("CPU flags", "cpu-flags"); + TARGET_VIEW_STRING("CPU flags (override)", "cpu-flags-override"); 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", ", "); @@ -258,6 +260,8 @@ static void view_target(BuildParseContext context, JSONObject *target, bool verb TARGET_VIEW_BOOL("Strip unused code/globals", "strip-unused"); TARGET_VIEW_INTEGER("Preferred symtab size", "symtab"); TARGET_VIEW_STRING("Target", "target"); + TARGET_VIEW_SETTING("RISC-V CPU level", "riscv-cpu", riscv_cpu_set); + TARGET_VIEW_SETTING("RISC-V ABI", "riscv-abi", riscv_abi); 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"); @@ -559,6 +563,7 @@ void view_project(BuildOptions *build_options) VIEW_STRING("Android API version", "android-api"); VIEW_STRING("Android NDK directory", "android-ndk"); VIEW_STRING("CPU name", "cpu"); + VIEW_STRING("CPU flags", "cpu-flags"); VIEW_SETTING("Debug level", "debug-info", debug_levels); VIEW_STRING_ARRAY("Scripts to run", "exec", ", "); VIEW_STRING_ARRAY("Enabled features", "features", ", "); @@ -587,6 +592,8 @@ void view_project(BuildOptions *build_options) VIEW_BOOL("Strip unused code/globals", "strip-unused"); VIEW_INTEGER("Preferred symtab size", "symtab"); VIEW_STRING("Target", "target"); + VIEW_SETTING("RISC-V CPU level", "riscv-cpu", riscv_cpu_set); + VIEW_SETTING("RISC-V ABI", "riscv-abi", riscv_abi); VIEW_STRING("Test function override", "testfn"); VIEW_BOOL("Integers panic on wrapping", "trap-on-wrap"); VIEW_BOOL("Include standard library", "use-stdlib");