From 88b2dc547e83a3196e55de36f8abb759f697117c Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Sat, 13 Jul 2019 22:46:39 +0200 Subject: [PATCH] Project setup --- .gitignore | 1 + CMakeLists.txt | 2 +- build/build_options.c | 7 ++ build/build_options.h | 21 +++++ build/project_creation.c | 129 ++++++++++++++++++++++++++++ build/project_creation.h | 8 ++ main.c | 181 ++++++++++++++++++++++++++++++++++++++- utils/errors.c | 5 ++ utils/errors.h | 7 ++ utils/file_utils.c | 20 +++++ utils/file_utils.h | 8 ++ utils/string_utils.c | 6 ++ utils/string_utils.h | 26 ++++++ 13 files changed, 416 insertions(+), 5 deletions(-) create mode 100644 build/build_options.c create mode 100644 build/build_options.h create mode 100644 build/project_creation.c create mode 100644 build/project_creation.h create mode 100644 utils/errors.c create mode 100644 utils/errors.h create mode 100644 utils/file_utils.c create mode 100644 utils/file_utils.h create mode 100644 utils/string_utils.c create mode 100644 utils/string_utils.h diff --git a/.gitignore b/.gitignore index c6127b38c..2f62d91eb 100644 --- a/.gitignore +++ b/.gitignore @@ -50,3 +50,4 @@ modules.order Module.symvers Mkfile.old dkms.conf +cmake-build-debug/ \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 84f306156..78aba0016 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,4 +3,4 @@ project(c3c C) set(CMAKE_C_STANDARD 11) -add_executable(c3c main.c) \ No newline at end of file +add_executable(c3c main.c build/build_options.c build/build_options.h build/project_creation.c build/project_creation.h utils/string_utils.c utils/string_utils.h utils/file_utils.c utils/file_utils.h utils/errors.c utils/errors.h) \ No newline at end of file diff --git a/build/build_options.c b/build/build_options.c new file mode 100644 index 000000000..63b43ebca --- /dev/null +++ b/build/build_options.c @@ -0,0 +1,7 @@ +// Copyright (c) 2019 Christoffer Lerno. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "build_options.h" + +BuildOptions build_options; \ No newline at end of file diff --git a/build/build_options.h b/build/build_options.h new file mode 100644 index 000000000..355c011e0 --- /dev/null +++ b/build/build_options.h @@ -0,0 +1,21 @@ +#pragma once + +// Copyright (c) 2019 Christoffer Lerno. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#define MAX_LIB_DIRS 1024 +#define MAX_FILES 2048 + +typedef struct +{ + const char* lib_dir[MAX_LIB_DIRS]; + int lib_count; + const char* files[MAX_FILES]; + int file_count; + const char* project_name; + const char* path; + const char* original_path; +} BuildOptions; + +extern BuildOptions build_options; \ No newline at end of file diff --git a/build/project_creation.c b/build/project_creation.c new file mode 100644 index 000000000..b126bbc0a --- /dev/null +++ b/build/project_creation.c @@ -0,0 +1,129 @@ +// Copyright (c) 2019 Christoffer Lerno. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include +#include +#include +#include +#include +#include "project_creation.h" +#include "build_options.h" +#include "string.h" +#include "../utils/string_utils.h" + +const char* TOML = + "[[executable]]\n" + "# name of the target\n" + "name = \"%s\"\n" + "# version using semantic versioning\n" + "version = \"0.1.0\"\n" + "# authors, optionally with email\n" + "authors = [\"John Doe \"]\n" + "# language version of C3\n" + "langrev = \"1\"\n" + "# warnings used\n" + "warnings = [\"no-unused\"]\n" + "# sources compiled\n" + "sources = [\"src/**\"]\n" + "# libraries to use\n" + "libs = [\"lib/**\"]\n"; + +void create_project(void) +{ + for (int i = 0; ; i++) + { + char c = build_options.project_name[i]; + if (c == '\0') break; + if (!is_alphanum_(c)) + { + fprintf(stderr, "'%s' is not a valid project name.\n", build_options.project_name); + exit(EXIT_FAILURE); + } + } + + if (chdir(build_options.path)) + { + fprintf(stderr, "Can't open path %s\n", build_options.path); + exit(EXIT_FAILURE); + } + + int error = mkdir(build_options.project_name, 0755); + if (error) + { + fprintf(stderr, "Could not create directory %s: %s\n", build_options.project_name, strerror(errno)); + exit(EXIT_FAILURE); + } + + if (chdir(build_options.project_name)) goto ERROR; + + FILE *file = fopen("LICENCE", "a"); + if (!file) goto ERROR; + fclose(file); + + file = fopen("README.md", "a"); + if (!file) goto ERROR; + fclose(file); + + file = fopen("project.toml", "a"); + if (!file) goto ERROR; + fprintf(file, TOML, build_options.project_name); + fclose(file); + + if (mkdir("lib", 0755)) goto ERROR; + + if (mkdir("build", 0755)) goto ERROR; + + if (mkdir("resources", 0755)) goto ERROR; + + if (mkdir("test", 0755)) goto ERROR; + + chdir("test"); + + if (mkdir(build_options.project_name, 0755)) goto ERROR; + + chdir(".."); + + if (mkdir("docs", 0755)) goto ERROR; + + chdir("docs"); + + file = fopen("about.md", "a"); + if (!file) goto ERROR; + fclose(file); + + if (mkdir("src", 0755)) goto ERROR; + + chdir("src"); + + file = fopen("index.html", "a"); + if (!file) goto ERROR; + fclose(file); + + chdir("../.."); + + if (mkdir("src", 0755)) goto ERROR; + + chdir("src"); + + if (mkdir(build_options.project_name, 0755)) goto ERROR; + + chdir(build_options.project_name); + + file = fopen("main.c3", "a"); + if (!file) goto ERROR; + fclose(file); + + chdir("../.."); + + printf("Project '%s' created.\n", build_options.project_name); + exit(EXIT_SUCCESS); + +ERROR: + fprintf(stderr, "Err: %s\n", strerror(errno)); + + printf("Something went wrong creating the project."); + chdir(build_options.path); + rmdir(build_options.project_name); + exit(EXIT_FAILURE); +} diff --git a/build/project_creation.h b/build/project_creation.h new file mode 100644 index 000000000..1ead0ec40 --- /dev/null +++ b/build/project_creation.h @@ -0,0 +1,8 @@ +#pragma once + +// Copyright (c) 2019 Christoffer Lerno. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + + +void create_project(void); \ No newline at end of file diff --git a/main.c b/main.c index 8321e25d3..fac4932bb 100644 --- a/main.c +++ b/main.c @@ -1,6 +1,179 @@ #include +#include +#include +#include +#include +#include +#include "build/build_options.h" +#include "build/project_creation.h" +#include "utils/errors.h" + +static void usage(const char* name) { + fprintf(stderr, "Usage: %s \n", name); + fprintf(stderr, "Options:\n"); + fprintf(stderr, " --lib - use this directory as the c3 library path\n"); + fprintf(stderr, " --path - use this as the base directory for the current command\n"); + exit(EXIT_SUCCESS); +} + +typedef enum +{ + COMMAND_COMPILE, + COMMAND_NEW, +} CompilerCommand; + +static CompilerCommand command; + +static int arg_index; +static int arg_count; +static const char** args; +static const char* current_arg; + +static void select_compiler_command(CompilerCommand new_command) +{ + if (command != COMMAND_COMPILE) + { + fprintf(stderr, "Please select only one command\n"); + exit(EXIT_FAILURE); + } + command = new_command; +} + +static const char* check_dir(const char *path) +{ + if (chdir(path) == -1) error_exit("The path \"%s\" does not point to a valid directory.", path); + int err = chdir(build_options.original_path); + assert(!err); + return path; +} + +static inline bool at_end() +{ + return arg_index == arg_count - 1; +} + +static inline const char* next_arg() +{ + assert(!at_end()); + current_arg = args[++arg_index]; + return current_arg; +} + + +static inline bool next_is_arg() +{ + return args[arg_index + 1][0] == '-'; +} + +static inline bool match_longopt(const char* name) +{ + return strcmp(¤t_arg[2], name) == 0; +} + +static inline bool match_shortopt(const char* name) +{ + return strcmp(¤t_arg[1], name) == 0; +} + + +void append_file() +{ + if (build_options.file_count == MAX_FILES) + { + fprintf(stderr, "Max %d files may be specified\n", MAX_FILES); + exit(EXIT_FAILURE); + } + build_options.files[build_options.file_count++] = current_arg; +} + +static void parse_option() +{ + switch (current_arg[1]) + { + case 'h': + break; + case 'n': + if (match_shortopt("new")) + { + select_compiler_command(COMMAND_NEW); + if (at_end() || next_is_arg()) error_exit("Expected a project name after -new"); + build_options.project_name = next_arg(); + return; + } + break; + case '-': + if (match_longopt("about")) + { + fprintf(stderr, "The C3 Compiler\n"); + fprintf(stderr, "\nC3 is an evolution of C.\n"); + exit(0); + } + if (match_longopt("lib")) + { + if (at_end() || next_is_arg()) error_exit("error: --lib needs a directory."); + if (current_arg[0] == '-') error_exit("Expected a directory after --lib."); + if (build_options.lib_count == MAX_LIB_DIRS) error_exit("Max %d libraries may be specified.", MAX_LIB_DIRS); + build_options.lib_dir[build_options.lib_count++] = check_dir(next_arg()); + return; + } + if (match_longopt("path")) + { + if (at_end() || next_is_arg()) error_exit("error: --path needs a directory."); + build_options.path = check_dir(next_arg()); + return; + } + if (match_longopt("help")) + { + break; + } + break; + default: + break; + + } + usage(args[0]); +} + +static void handle_compile_command() +{ + printf("TODO\n"); + exit(EXIT_SUCCESS); +} + +static void parse_arguments() +{ + command = COMMAND_COMPILE; + for (arg_index = 1; arg_index < arg_count; arg_index++) + { + current_arg = args[arg_index]; + if (current_arg[0] == '-') + { + parse_option(); + continue; + } + append_file(); + } + switch (command) + { + case COMMAND_NEW: + create_project(); + break; + case COMMAND_COMPILE: + handle_compile_command(); + break; + default: + exit(EXIT_FAILURE); + } +} + + +int main(int argc, const char *argv[]) +{ + build_options.path = "."; + build_options.original_path = getcwd(NULL, 0); + arg_count = argc; + args = argv; + parse_arguments(); + return EXIT_SUCCESS; +} -int main() { - printf("Hello, World!\n"); - return 0; -} \ No newline at end of file diff --git a/utils/errors.c b/utils/errors.c new file mode 100644 index 000000000..510abde73 --- /dev/null +++ b/utils/errors.c @@ -0,0 +1,5 @@ +// Copyright (c) 2019 Christoffer Lerno. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "errors.h" diff --git a/utils/errors.h b/utils/errors.h new file mode 100644 index 000000000..812989395 --- /dev/null +++ b/utils/errors.h @@ -0,0 +1,7 @@ +#pragma once + +// Copyright (c) 2019 Christoffer Lerno. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#define error_exit(...) do { fprintf(stderr, __VA_ARGS__); fprintf(stderr, "\n"); exit(EXIT_FAILURE); } while(0) diff --git a/utils/file_utils.c b/utils/file_utils.c new file mode 100644 index 000000000..bd57da698 --- /dev/null +++ b/utils/file_utils.c @@ -0,0 +1,20 @@ +// Copyright (c) 2019 Christoffer Lerno. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "file_utils.h" +#include +#include + +const char* expand_path(const char* path) +{ + if (path[0] == '~' && path[1] == '/') + { + // Ignore leak. + char *ret = NULL; + char *home = getenv("HOME"); + if (!home || asprintf(&ret, "%s%s", home, &path[1]) == -1) return &path[2]; + return ret; + } + return path; +} \ No newline at end of file diff --git a/utils/file_utils.h b/utils/file_utils.h new file mode 100644 index 000000000..cd2143979 --- /dev/null +++ b/utils/file_utils.h @@ -0,0 +1,8 @@ +#pragma once + +// Copyright (c) 2019 Christoffer Lerno. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + + +const char* expand_path(const char* path); diff --git a/utils/string_utils.c b/utils/string_utils.c new file mode 100644 index 000000000..0996858f9 --- /dev/null +++ b/utils/string_utils.c @@ -0,0 +1,6 @@ +// Copyright (c) 2019 Christoffer Lerno. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "string_utils.h" + diff --git a/utils/string_utils.h b/utils/string_utils.h new file mode 100644 index 000000000..d9101e93d --- /dev/null +++ b/utils/string_utils.h @@ -0,0 +1,26 @@ +#pragma once + +// Copyright (c) 2019 Christoffer Lerno. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + + +#include + +static inline bool is_lower(char c) +{ + return c >= 'a' && c <= 'z'; +} + +static inline bool is_upper(char c) +{ + return c >= 'A' && c <= 'Z'; +} + +static inline bool is_alphanum_(char c) +{ + return (c >= 'a' && c <= 'z') + || (c >= 'A' && c <= 'Z') + || (c >= '0' && c <= '9') + || c == '_'; +} \ No newline at end of file