added: `onyx pkg new`
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Sat, 13 May 2023 17:37:48 +0000 (12:37 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Sat, 13 May 2023 17:37:48 +0000 (12:37 -0500)
build.sh
scripts/default.json [new file with mode: 0644]
scripts/onyx-pkg.onyx

index 7f4c50148f2ed0cf6e7237fd9f2cb0da3726bb4f..823497b932b3f5bf82dbfe0f968e9f3d29535ba6 100755 (executable)
--- a/build.sh
+++ b/build.sh
@@ -11,7 +11,9 @@ sudo cp -r ./core/ "$CORE_DIR"
 
 
 sudo mkdir -p "$CORE_DIR/tools"
+sudo mkdir -p "$CORE_DIR/tools/pkg_templates"
 sudo cp ./scripts/onyx-pkg.onyx "$CORE_DIR/tools"
+sudo cp ./scripts/default.json "$CORE_DIR/tools/pkg_templates"
 
 # This is a development feature to allow for quickly reinstalling core libraries
 # without have to recompile the entire compiler
diff --git a/scripts/default.json b/scripts/default.json
new file mode 100644 (file)
index 0000000..85e0c55
--- /dev/null
@@ -0,0 +1,28 @@
+{
+    "variables": {
+        "name": {
+            "type": "string",
+            "description": "Name of the project"
+        },
+        "description": {
+            "type": "string",
+            "description": "Description of the project"
+        },
+        "url": {
+            "type": "string",
+            "description": "Git repository for project"
+        },
+        "author": {
+            "type": "string",
+            "description": "Author name"
+        }
+    },
+    "files": {
+        ".gitignore": "*.wasm",
+        "onyx-lsp.ini": "[lsp]\nmode=project\nonyxFiles=src/main.onyx\nworkingDir=.",
+        "onyx-pkg.ini": "[metadata]\nname={{name}}\ndescription={{description}}\nurl={{url}}\nauthor={{author}}\nversion=0.0.1\n",
+        "src": {
+            "main.onyx": "use core {*}\n\nmain :: () {\n    println(\"Hello Onyx!\");\n}\n"
+        }
+    }
+}
index 1201edee32db60640a6c7a32cb3eb60ed9df2b20..6c33d27de2cbc147b5fae5f6cc381de3a2a1a673 100644 (file)
@@ -11,6 +11,19 @@ Known_Repositories :: str.[
     "git://onyxlang.io/repo/{}"
 ]
 
+
+//
+// NOTE: This returns a string that ends with the path separator.
+Template_Directory :: () -> str {
+    // TODO: Write a path library for this...
+    path_sep := '/';
+    if runtime.compiler_os == .Windows do path_sep = '\\';
+
+    file := #file;
+    path := file[0 .. string.last_index_of(file, path_sep)];
+    return aprintf("{}{}pkg_templates{}", path, path_sep, path_sep);
+}
+
 Version :: SemVer.{0, 1, 1}
 
 
@@ -469,12 +482,104 @@ run_test_command :: (args: [] cstr) {
     }
 }
 
-#tag Command.{ "new", "Create a new project in the current directory." }
+#tag Command.{
+    "new", "Create a new project from a template in the current directory.", "[template_name] | --list",
+"""
+template_name    Template name to create. Set to 'default' if unspecified.
+
+""",
+    require_config_file = false
+}
 run_new_command :: (args: [] cstr) {
-    // Create onyx-pkg.ini
-    // Create onyx-lsp.ini
-    // Create src/main.onyx
-    // Create .gitignore
+    if args.count >= 1 {
+        if string.as_str(args[0]) == "--list" {
+            printf("List of installed templates\n");
+            for os.list_directory(Template_Directory()) {
+                if !string.ends_with(it->name(), ".json") do continue;
+
+                printf("    {}\n", os.path_basename(it->name()));
+            }
+            return;
+        }
+    }
+
+    if os.list_directory(".")->count(x => true) > 0 {
+        error_print("Refusing to initialize project in non-empty directory.\n");
+        return;
+    }
+
+    template_name := "default";
+
+    if args.count >= 1 {
+        template_name = string.as_str(args[0]);
+    }
+
+    template_dir := Template_Directory();
+    template_file := tprintf("{}{}.json", template_dir, template_name);
+    if !os.file_exists(template_file) {
+        error_print("Template '{}' not found in {}\n", template_name, template_dir);
+        return;
+    }
+
+    use core.encoding {json}
+    template, err := json.decode_with_error(os.get_contents(template_file));
+    if err->has_error() {
+        error_print("Failed to parse template file.\n");
+        print(err->message());
+        return;
+    }
+
+    vars := make(Map(str, str));
+    input := io.reader_make(&stdio.stream);
+
+    template_variables := template.root["variables"]->as_map();
+    for template_variables->as_iter() {
+        assert(it.value["type"]->as_str() == "string", "Only string types are supported right now in template variables.");
+
+        printf("{}: ", it.value["description"]->as_str());
+        line := input->read_line(consume_newline=true, allocator=context.temp_allocator)
+                |> string.strip_whitespace();
+
+        vars->put(it.key, line);
+    }
+
+    populate_directory(".", template.root["files"], &vars);
+
+    populate_directory :: (dir: str, files: json.Value, vars: &Map(str, str)) {
+        for files->as_map_iter() {
+            destination := os.path_join(dir, it.first);
+            switch it.second->type() {
+                case .String {
+                    info_print("Creating", tprintf("{}\n", destination));
+
+                    contents := process_contents(it.second->as_str(), vars);
+                    for os.with_file(destination, .Write) {
+                        io.stream_write(it, contents);
+                    }
+                }
+
+                case .Object {
+                    os.dir_create(destination);
+                    populate_directory(destination, it.second, vars);
+                }
+            }
+        }
+    }
+
+    process_contents :: (contents: str, vars: &Map(str, str)) -> str {
+        output: dyn_str;
+
+        to_process := contents;
+        while to_process {
+            to_output, to_process~ := string.bisect(to_process, "{{");
+            string.append(&output, to_output);
+
+            var_name, to_process~ := string.bisect(to_process, "}}");
+            string.append(&output, vars->get(var_name));
+        }
+
+        return output;
+    }
 }
 
 
@@ -592,13 +697,15 @@ run_native_library_installation :: (folder: str) -> (bool, str) {
 
         {
             build_proc     := os.process_spawn(cmd, args, starting_directory=installed_dest);
+            build_result   := os.process_wait(&build_proc);
+
             build_reader := io.reader_make(&build_proc);
             defer io.reader_free(&build_reader);
+            build_info := build_reader->read_all();
 
-            build_result   := os.process_wait(&build_proc);
             if build_result != .Success {
                 error_print("Failed to build native library in {}.\n", folder);
-                return false, build_reader->read_all();
+                return false, build_info;
             }
         }