diff --git a/releasenotes.md b/releasenotes.md index cbf1c7776..9b5ef3c6b 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -3,6 +3,7 @@ ## 0.5.0 Change List ### Changes / improvements +- Added `init-lib` to simplify library creation. - Local `const` work like namespaced global `const`. - Added `$$atomic_fetch_*` builtins. - vectors may now contain pointers. diff --git a/src/build/build.h b/src/build/build.h index 96198a94c..35bb081b7 100644 --- a/src/build/build.h +++ b/src/build/build.h @@ -28,6 +28,7 @@ typedef enum COMMAND_COMPILE_TEST, COMMAND_GENERATE_HEADERS, COMMAND_INIT, + COMMAND_INIT_LIB, COMMAND_BUILD, COMMAND_COMPILE_RUN, COMMAND_STATIC_LIB, @@ -550,4 +551,4 @@ ArchOsTarget arch_os_target_from_string(const char *target); bool command_accepts_files(CompilerCommand command); void update_build_target_with_opt_level(BuildTarget *target, OptimizationSetting level); void create_project(BuildOptions *build_options); - +void create_library(BuildOptions *build_options); diff --git a/src/build/build_options.c b/src/build/build_options.c index c704904ba..45fe89dde 100644 --- a/src/build/build_options.c +++ b/src/build/build_options.c @@ -53,6 +53,7 @@ static void usage(void) OUTPUT(""); OUTPUT(" compile [ ...] Compile files without a project into an executable."); OUTPUT(" init Initialize a new project structure."); + OUTPUT(" init-lib Initialize a new library structure."); OUTPUT(" build [] Build the target in the current project."); OUTPUT(" benchmark Run the benchmarks in the current project."); OUTPUT(" test Run the unit tests in the current project."); @@ -256,6 +257,13 @@ static void parse_command(BuildOptions *options) options->project_name = next_arg(); return; } + if (arg_match("init-lib")) + { + options->command = COMMAND_INIT_LIB; + if (at_end() || next_is_opt()) error_exit("Expected a library name after init"); + options->project_name = next_arg(); + return; + } if (arg_match("utest")) { options->command = COMMAND_UNIT_TEST; diff --git a/src/build/builder.c b/src/build/builder.c index cc04405ed..262931c44 100644 --- a/src/build/builder.c +++ b/src/build/builder.c @@ -75,6 +75,7 @@ bool command_accepts_files(CompilerCommand command) case COMMAND_MISSING: case COMMAND_GENERATE_HEADERS: case COMMAND_INIT: + case COMMAND_INIT_LIB: case COMMAND_BUILD: case COMMAND_RUN: case COMMAND_CLEAN_RUN: diff --git a/src/build/project_creation.c b/src/build/project_creation.c index 0860130c0..d5bedc9cc 100644 --- a/src/build/project_creation.c +++ b/src/build/project_creation.c @@ -123,7 +123,25 @@ const char* JSON_DYNAMIC = " // See resources/examples/project_all_settings.json and 'c3c --list-project-properties' to see more properties.\n" "}"; -const char* MAIN_TEMPLATE = +const char *MAINFEST_TEMPLATE = + "{\n" + " \"provides\" : \"%s\",\n" + " \"targets\" : {\n" + "%s" + " }\n" + "}"; + +const char *MAINIFEST_TARGET = + " \"%s\" : {\n" + " // Extra flags to the linker for this target:\n" + " \"linkflags\" : [],\n" + " // C3 libraries this target depends on:\n" + " \"dependencies\" : [],\n" + " // The external libraries to link for this target:\n" + " \"linked-libs\" : []\n" + " },\n"; + +const char *MAIN_TEMPLATE = "module %s;\n" "import std::io;\n" "\n" @@ -133,6 +151,72 @@ const char* MAIN_TEMPLATE = "\treturn 0;\n" "}\n"; +const char* MAIN_INTERFACE_TEMPLATE = + "module %s;\n" + "\n" + "// extern fn int some_library_function();\n"; + +const char* DEFAULT_TARGETS[] = { + "freebsd-x64", + "linux-aarch64", + "linux-riscv32", + "linux-riscv64", + "linux-x86", + "linux-x64", + "macos-aarch64", + "macos-x64", + "netbsd-x64", + "openbsd-x64", + "wasm32", + "wasm64", + "windows-aarch64", + "windows-x64" +}; + +const char *LIB_README = "Welcome to the %s library.\n"; + +static bool check_name(const char *name); +static void exit_fail(const char *fmt, ...); +static void delete_dir_and_exit(BuildOptions *build_options, const char *fmt, ...); +static void mkdir_or_fail(BuildOptions *build_options, const char *name); +static void chdir_or_fail(BuildOptions *build_options, const char *name); +static void create_file_or_fail(BuildOptions *build_options, const char *filename, const char *fmt, ...); +static const char *module_name(BuildOptions *build_options); + +void create_library(BuildOptions *build_options) +{ + if (!check_name(build_options->project_name)) + { + exit_fail("'%s' is not a valid library name.", build_options->project_name); + } + if (!dir_change(build_options->path)) + { + exit_fail("Can't open path %s", build_options->path); + } + + if (!dir_make(build_options->project_name)) + { + exit_fail("Could not create directory %s.", build_options->project_name); + } + + chdir_or_fail(build_options, build_options->project_name); + create_file_or_fail(build_options, "LICENSE", NULL); + create_file_or_fail(build_options, "README.md", LIB_README, build_options->project_name); + mkdir_or_fail(build_options, "run"); + scratch_buffer_clear(); + scratch_buffer_printf("%s.c3i", build_options->project_name); + const char *interface_file = scratch_buffer_copy(); + create_file_or_fail(build_options, interface_file, MAIN_INTERFACE_TEMPLATE, module_name(build_options)); + scratch_buffer_clear(); + for (int i = 0; i < sizeof(DEFAULT_TARGETS) / sizeof(char*); i++) + { + const char *target = DEFAULT_TARGETS[i]; + scratch_buffer_printf(MAINIFEST_TARGET, target); + mkdir_or_fail(build_options, target); + } + create_file_or_fail(build_options, "manifest.json", MAINFEST_TEMPLATE, build_options->project_name, scratch_buffer_to_string()); +} + void create_project(BuildOptions *build_options) { const char *template; @@ -153,58 +237,45 @@ void create_project(BuildOptions *build_options) size_t len; template = file_read_all(build_options->template, &len); } - for (int i = 0; ; i++) + if (!check_name(build_options->project_name)) { - char c = build_options->project_name[i]; - if (c == '\0') break; - if (!char_is_alphanum_(c)) - { - fprintf(stderr, "'%s' is not a valid project name.\n", build_options->project_name); - exit_compiler(EXIT_FAILURE); - } + error_exit("'%s' is not a valid project name.", build_options->project_name); } if (!dir_change(build_options->path)) { - fprintf(stderr, "Can't open path %s\n", build_options->path); - exit_compiler(EXIT_FAILURE); + error_exit("Can't open path '%s'.", build_options->path); } if (!dir_make(build_options->project_name)) { - fprintf(stderr, "Could not create directory %s.\n", build_options->project_name); - exit_compiler(EXIT_FAILURE); + error_exit("Could not create directory '%s'.", build_options->project_name); } - if (!dir_change(build_options->project_name)) goto ERROR; + chdir_or_fail(build_options, build_options->project_name); + create_file_or_fail(build_options, "LICENSE", NULL); + create_file_or_fail(build_options, "README.md", NULL); + create_file_or_fail(build_options, template, build_options->project_name); + mkdir_or_fail(build_options, "build"); + mkdir_or_fail(build_options, "docs"); + mkdir_or_fail(build_options, "lib"); + mkdir_or_fail(build_options, "resources"); + mkdir_or_fail(build_options, "run"); + mkdir_or_fail(build_options, "src"); + chdir_or_fail(build_options, "src"); - if (!file_touch("LICENSE")) goto ERROR; + create_file_or_fail(build_options, MAIN_TEMPLATE, module_name(build_options)); + chdir_or_fail(build_options, ".."); + mkdir_or_fail(build_options, "test"); - if (!file_touch("README.md")) goto ERROR; + (void) printf("Project '%s' created.\n", build_options->project_name); + exit_compiler(COMPILER_SUCCESS_EXIT); +} +// Helper functions: - FILE *file = fopen("project.json", "a"); - if (!file) goto ERROR; - (void) fprintf(file, template, build_options->project_name); - if (fclose(file)) goto ERROR; - - if (!dir_make("build")) goto ERROR; - - if (!dir_make("docs")) goto ERROR; - - if (!dir_make("lib")) goto ERROR; - - if (!dir_make("resources")) goto ERROR; - - if (!dir_make("run")) goto ERROR; - - if (!dir_make("src")) goto ERROR; - - if (!dir_change("src")) goto ERROR; - - file = fopen("main.c3", "w"); - if (!file) goto ERROR; - +static const char *module_name(BuildOptions *build_options) +{ scratch_buffer_clear(); size_t len = strlen(build_options->project_name); bool has_char = false; @@ -227,22 +298,80 @@ void create_project(BuildOptions *build_options) scratch_buffer_append_char('_'); } if (!has_char) scratch_buffer_append("module"); - (void) fprintf(file, MAIN_TEMPLATE, scratch_buffer_to_string()); - if (fclose(file)) goto ERROR; + return scratch_buffer_to_string(); +} +static void create_file_or_fail(BuildOptions *build_options, const char *filename, const char *fmt, ...) +{ + if (!fmt) + { + if (!file_touch(filename)) + { + delete_dir_and_exit(build_options, "Could not create '%s' file.", filename); + } + return; + } + FILE *file = fopen(filename, "a"); + if (!file) + { + delete_dir_and_exit(build_options, "Couldn't create '%s' file.", filename); + } + va_list list; + va_start(list, fmt); + (void) vfprintf(file, fmt, list); + va_end(list); + if (fclose(file)) + { + delete_dir_and_exit(build_options, "Couldn't close the '%s' file.", filename); + } +} - if (!dir_change("..")) goto ERROR; +static bool check_name(const char *name) +{ + for (int i = 0; ; i++) + { + char c = name[i]; + if (c == '\0') break; + if (!char_is_alphanum_(c)) return false; + } + return true; +} - if (!dir_make("test")) goto ERROR; +static void chdir_or_fail(BuildOptions *build_options, const char *name) +{ + if (!dir_change(name)) + { + delete_dir_and_exit(build_options, "Failed to open directory '%s'.", name); + } +} - (void) printf("Project '%s' created.\n", build_options->project_name); - exit_compiler(COMPILER_SUCCESS_EXIT); +static void exit_fail(const char *fmt, ...) +{ + va_list list; + va_start(list, fmt); + vfprintf(stderr, fmt, list); + va_end(list); + fputs("", stderr); + exit_compiler(EXIT_FAILURE); +} -ERROR: - fprintf(stderr, "Error creating the project.\n"); +static void delete_dir_and_exit(BuildOptions *build_options, const char *fmt, ...) +{ + va_list list; + va_start(list, fmt); if (dir_change(build_options->path)) { (void)rmdir(build_options->project_name); } + vfprintf(stderr, fmt, list); + va_end(list); + fputs("", stderr); exit_compiler(EXIT_FAILURE); } +static void mkdir_or_fail(BuildOptions *build_options, const char *name) +{ + if (!dir_make(name)) + { + delete_dir_and_exit(build_options, "Failed to create directory '%s'.", name); + } +} diff --git a/src/main.c b/src/main.c index 4c177bef9..50d390911 100644 --- a/src/main.c +++ b/src/main.c @@ -55,6 +55,9 @@ int main_real(int argc, const char *argv[]) case COMMAND_INIT: create_project(&build_options); break; + case COMMAND_INIT_LIB: + create_library(&build_options); + break; case COMMAND_UNIT_TEST: compiler_tests(); break; diff --git a/src/version.h b/src/version.h index 0753155cb..c4937ce25 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define COMPILER_VERSION "0.4.664" \ No newline at end of file +#define COMPILER_VERSION "0.4.665" \ No newline at end of file