Files
c3c/src/build/project_manipulation.c

609 lines
20 KiB
C

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