Merge branch 'refs/heads/feature/user-cli' into feature/docs-graalvm-package
This commit is contained in:
commit
72d187ab76
3
.gitignore
vendored
3
.gitignore
vendored
@ -37,3 +37,6 @@ target/
|
||||
/.idea/
|
||||
/Snow.tar
|
||||
/src/main/java/org/jcnc/snow/compiler/ir.tar
|
||||
|
||||
### Snow 虚拟机指令 ###
|
||||
*.water
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Demo1" type="Application" factoryName="Application" folderName="Demo" activateToolWindowBeforeRun="false">
|
||||
<option name="ALTERNATIVE_JRE_PATH" value="graalvm-ce-23" />
|
||||
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.compiler.cli.SnowCompiler" />
|
||||
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
|
||||
<module name="Snow" />
|
||||
<option name="PROGRAM_PARAMETERS" value="-d playground/Demo1" />
|
||||
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo1" />
|
||||
<extension name="coverage">
|
||||
<pattern>
|
||||
<option name="PATTERN" value="org.jcnc.snow.compiler.parser.preprocessor.lexer.impl.api.*" />
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Demo2" type="Application" factoryName="Application" folderName="Demo" activateToolWindowBeforeRun="false">
|
||||
<option name="ALTERNATIVE_JRE_PATH" value="graalvm-ce-23" />
|
||||
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.compiler.cli.SnowCompiler" />
|
||||
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
|
||||
<module name="Snow" />
|
||||
<option name="PROGRAM_PARAMETERS" value="-d playground/Demo2" />
|
||||
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo2" />
|
||||
<extension name="coverage">
|
||||
<pattern>
|
||||
<option name="PATTERN" value="org.jcnc.snow.compiler.parser.preprocessor.lexer.impl.api.*" />
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Demo3" type="Application" factoryName="Application" folderName="Demo" activateToolWindowBeforeRun="false">
|
||||
<option name="ALTERNATIVE_JRE_PATH" value="graalvm-ce-23" />
|
||||
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.compiler.cli.SnowCompiler" />
|
||||
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
|
||||
<module name="Snow" />
|
||||
<option name="PROGRAM_PARAMETERS" value="-d playground/Demo3" />
|
||||
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo3" />
|
||||
<extension name="coverage">
|
||||
<pattern>
|
||||
<option name="PATTERN" value="org.jcnc.snow.compiler.parser.preprocessor.lexer.impl.api.*" />
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Demo4" type="Application" factoryName="Application" folderName="Demo" activateToolWindowBeforeRun="false">
|
||||
<option name="ALTERNATIVE_JRE_PATH" value="graalvm-ce-23" />
|
||||
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.compiler.cli.SnowCompiler" />
|
||||
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
|
||||
<module name="Snow" />
|
||||
<option name="PROGRAM_PARAMETERS" value="-d playground/Demo4" />
|
||||
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo4" />
|
||||
<extension name="coverage">
|
||||
<pattern>
|
||||
<option name="PATTERN" value="org.jcnc.snow.compiler.parser.preprocessor.lexer.impl.api.*" />
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Demo5" type="Application" factoryName="Application" folderName="Demo" activateToolWindowBeforeRun="false">
|
||||
<option name="ALTERNATIVE_JRE_PATH" value="graalvm-ce-23" />
|
||||
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.compiler.cli.SnowCompiler" />
|
||||
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
|
||||
<module name="Snow" />
|
||||
<option name="PROGRAM_PARAMETERS" value="-d playground/Demo5" />
|
||||
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo5" />
|
||||
<extension name="coverage">
|
||||
<pattern>
|
||||
<option name="PATTERN" value="org.jcnc.snow.compiler.parser.preprocessor.lexer.impl.api.*" />
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Demo6" type="Application" factoryName="Application" folderName="Demo" activateToolWindowBeforeRun="false">
|
||||
<option name="ALTERNATIVE_JRE_PATH" value="graalvm-ce-23" />
|
||||
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.compiler.cli.SnowCompiler" />
|
||||
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
|
||||
<module name="Snow" />
|
||||
<option name="PROGRAM_PARAMETERS" value="-d playground/Demo6" />
|
||||
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo6" />
|
||||
<extension name="coverage">
|
||||
<pattern>
|
||||
<option name="PATTERN" value="org.jcnc.snow.compiler.parser.preprocessor.lexer.impl.api.*" />
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Demo7" type="Application" factoryName="Application" folderName="Demo" activateToolWindowBeforeRun="false">
|
||||
<option name="ALTERNATIVE_JRE_PATH" value="graalvm-ce-23" />
|
||||
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.compiler.cli.SnowCompiler" />
|
||||
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
|
||||
<module name="Snow" />
|
||||
<option name="PROGRAM_PARAMETERS" value="-d playground/Demo7" />
|
||||
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo7" />
|
||||
<extension name="coverage">
|
||||
<pattern>
|
||||
<option name="PATTERN" value="org.jcnc.snow.compiler.parser.preprocessor.lexer.impl.api.*" />
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Demo8" type="Application" factoryName="Application" folderName="Demo" activateToolWindowBeforeRun="false">
|
||||
<option name="ALTERNATIVE_JRE_PATH" value="graalvm-ce-23" />
|
||||
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.compiler.cli.SnowCompiler" />
|
||||
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
|
||||
<module name="Snow" />
|
||||
<option name="PROGRAM_PARAMETERS" value="-d playground/Demo8" />
|
||||
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo8" />
|
||||
<extension name="coverage">
|
||||
<pattern>
|
||||
<option name="PATTERN" value="org.jcnc.snow.compiler.parser.preprocessor.lexer.impl.api.*" />
|
||||
|
||||
16
.run/Help.run.xml
Normal file
16
.run/Help.run.xml
Normal file
@ -0,0 +1,16 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Help" type="Application" factoryName="Application">
|
||||
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
|
||||
<module name="Snow" />
|
||||
<option name="PROGRAM_PARAMETERS" value="--help" />
|
||||
<extension name="coverage">
|
||||
<pattern>
|
||||
<option name="PATTERN" value="org.jcnc.snow.cli.SnowCLI" />
|
||||
<option name="ENABLED" value="true" />
|
||||
</pattern>
|
||||
</extension>
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
16
.run/Version.run.xml
Normal file
16
.run/Version.run.xml
Normal file
@ -0,0 +1,16 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Version" type="Application" factoryName="Application">
|
||||
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
|
||||
<module name="Snow" />
|
||||
<option name="PROGRAM_PARAMETERS" value="--version" />
|
||||
<extension name="coverage">
|
||||
<pattern>
|
||||
<option name="PATTERN" value="org.jcnc.snow.cli.SnowCLI" />
|
||||
<option name="ENABLED" value="true" />
|
||||
</pattern>
|
||||
</extension>
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
14
pom.xml
14
pom.xml
@ -7,9 +7,11 @@
|
||||
|
||||
<groupId>org.jcnc.snow</groupId>
|
||||
<artifactId>Snow</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<version>0.4.0</version>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
<maven.compiler.source>24</maven.compiler.source>
|
||||
<maven.compiler.target>24</maven.compiler.target>
|
||||
<native.maven.plugin.version>0.10.5</native.maven.plugin.version>
|
||||
@ -17,6 +19,13 @@
|
||||
|
||||
<!-- 通用编译 & 打包插件 -->
|
||||
<build>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<filtering>true</filtering>
|
||||
</resource>
|
||||
</resources>
|
||||
|
||||
<plugins>
|
||||
<!-- Java 编译插件 -->
|
||||
<plugin>
|
||||
@ -35,8 +44,9 @@
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifest>
|
||||
<mainClass>org.jcnc.snow.compiler.cli.SnowCompiler</mainClass>
|
||||
<mainClass>org.jcnc.snow.cli.SnowCLI</mainClass>
|
||||
<addClasspath>true</addClasspath>
|
||||
<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
|
||||
</manifest>
|
||||
</archive>
|
||||
</configuration>
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
module org.jcnc.snow.compiler {
|
||||
uses org.jcnc.snow.cli.CLICommand;
|
||||
requires java.desktop;
|
||||
requires java.logging;
|
||||
exports org.jcnc.snow.compiler.ir.core;
|
||||
|
||||
45
src/main/java/org/jcnc/snow/cli/CLICommand.java
Normal file
45
src/main/java/org/jcnc/snow/cli/CLICommand.java
Normal file
@ -0,0 +1,45 @@
|
||||
package org.jcnc.snow.cli;
|
||||
|
||||
/**
|
||||
* 所有 CLI 子命令(如 compile、run 等)都必须实现的命令接口。
|
||||
* <p>
|
||||
* 实现类应为无状态(stateless)、线程安全(thread-safe)。
|
||||
* 可通过 {@link java.util.ServiceLoader ServiceLoader} 自动发现,
|
||||
* 或直接在 {@link SnowCLI} 中注册。
|
||||
* </p>
|
||||
*/
|
||||
public interface CLICommand {
|
||||
|
||||
/**
|
||||
* 获取命令的名称(如 "compile"、"run")。
|
||||
*
|
||||
* @return 命令名字符串
|
||||
*/
|
||||
String name();
|
||||
|
||||
/**
|
||||
* 获取命令的一行简介(用于 help 列表)。
|
||||
*
|
||||
* @return 命令描述字符串
|
||||
*/
|
||||
String description();
|
||||
|
||||
/**
|
||||
* 打印命令的专用 usage 信息(可选实现)。
|
||||
* <p>
|
||||
* 可覆盖此方法自定义帮助信息,默认无操作。
|
||||
* </p>
|
||||
*/
|
||||
default void printUsage() {
|
||||
// 默认实现为空,可由子类覆盖
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行命令逻辑。
|
||||
*
|
||||
* @param args 传递给子命令的参数(不含命令名本身)
|
||||
* @return 进程退出码(0 为成功,非 0 为错误)
|
||||
* @throws Exception 可抛出任意异常,框架会统一捕获和输出
|
||||
*/
|
||||
int execute(String[] args) throws Exception;
|
||||
}
|
||||
97
src/main/java/org/jcnc/snow/cli/SnowCLI.java
Normal file
97
src/main/java/org/jcnc/snow/cli/SnowCLI.java
Normal file
@ -0,0 +1,97 @@
|
||||
package org.jcnc.snow.cli;
|
||||
|
||||
import org.jcnc.snow.cli.commands.CompileCommand;
|
||||
import org.jcnc.snow.cli.commands.RunCommand;
|
||||
import org.jcnc.snow.cli.commands.VersionCommand;
|
||||
import org.jcnc.snow.cli.utils.CLIUtils;
|
||||
import org.jcnc.snow.cli.utils.VersionUtils;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* Snow 编程语言的命令行接口入口类。
|
||||
* <p>
|
||||
* 该类解析用户输入的命令行参数,调度相应的子命令(compile、run、version),
|
||||
* 并处理全局帮助和版本信息输出。
|
||||
* </p>
|
||||
*/
|
||||
public class SnowCLI {
|
||||
|
||||
/**
|
||||
* Snow 编程语言的当前版本号,从资源文件中加载。
|
||||
*/
|
||||
public static final String SNOW_VERSION = VersionUtils.loadVersion();
|
||||
|
||||
/**
|
||||
* 可用子命令名称与对应命令处理器的映射表。
|
||||
* 键为子命令名称("compile", "run", "version"),
|
||||
* 值为返回相应 {@link CLICommand} 实例的 Supplier。
|
||||
*/
|
||||
private static final Map<String, Supplier<CLICommand>> COMMANDS = Map.of(
|
||||
"compile", CompileCommand::new,
|
||||
"run", RunCommand::new,
|
||||
"version", VersionCommand::new,
|
||||
"init", org.jcnc.snow.cli.commands.InitCommand::new,
|
||||
"build", org.jcnc.snow.cli.commands.BuildCommand::new,
|
||||
"install", org.jcnc.snow.cli.commands.InstallCommand::new,
|
||||
"publish", org.jcnc.snow.cli.commands.PublishCommand::new,
|
||||
"clean", org.jcnc.snow.cli.commands.CleanCommand::new
|
||||
);
|
||||
|
||||
/**
|
||||
* 程序入口方法,解析并调度子命令。
|
||||
* <p>
|
||||
*
|
||||
* @param args 命令行参数数组
|
||||
* 无参数或首参数为帮助标志时,打印全局用法说明并退出
|
||||
* 首参数为版本标志时,打印版本信息并退出
|
||||
* 首参数为子命令名时,进一步解析该子命令的参数并执行
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
// 处理全局帮助
|
||||
if (args.length == 0 || CLIUtils.GLOBAL_HELP_FLAGS.contains(args[0])) {
|
||||
CLIUtils.printGeneralUsage(COMMANDS);
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
// 处理全局版本请求
|
||||
if (CLIUtils.GLOBAL_VERSION_FLAGS.contains(args[0])) {
|
||||
new VersionCommand().execute(new String[0]);
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
// 子命令名称
|
||||
String cmdName = args[0];
|
||||
Supplier<CLICommand> cmdSupplier = COMMANDS.get(cmdName);
|
||||
|
||||
// 未知子命令处理
|
||||
if (cmdSupplier == null) {
|
||||
System.err.println("Unknown command: " + cmdName);
|
||||
CLIUtils.printGeneralUsage(COMMANDS);
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
// 创建对应子命令实例
|
||||
CLICommand cmd = cmdSupplier.get();
|
||||
// 提取子命令参数
|
||||
String[] subArgs = Arrays.copyOfRange(args, 1, args.length);
|
||||
|
||||
// 如果子命令请求帮助,则打印该命令的用法说明并退出
|
||||
if (subArgs.length > 0 && CLIUtils.GLOBAL_HELP_FLAGS.contains(subArgs[0])) {
|
||||
cmd.printUsage();
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
// 执行子命令并根据返回的退出码退出
|
||||
try {
|
||||
int exitCode = cmd.execute(subArgs);
|
||||
System.exit(exitCode);
|
||||
} catch (Exception e) {
|
||||
// 捕获命令执行过程中的异常并打印错误消息
|
||||
System.err.println("Error: " + e.getMessage());
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
86
src/main/java/org/jcnc/snow/cli/commands/BuildCommand.java
Normal file
86
src/main/java/org/jcnc/snow/cli/commands/BuildCommand.java
Normal file
@ -0,0 +1,86 @@
|
||||
package org.jcnc.snow.cli.commands;
|
||||
|
||||
import org.jcnc.snow.cli.CLICommand;
|
||||
import org.jcnc.snow.pkg.dsl.CloudDSLParser;
|
||||
import org.jcnc.snow.pkg.lifecycle.LifecycleManager;
|
||||
import org.jcnc.snow.pkg.lifecycle.LifecyclePhase;
|
||||
import org.jcnc.snow.pkg.model.Project;
|
||||
import org.jcnc.snow.pkg.resolver.DependencyResolver;
|
||||
import org.jcnc.snow.pkg.tasks.CompileTask;
|
||||
import org.jcnc.snow.pkg.tasks.PackageTask;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
/**
|
||||
* CLI 命令:构建当前项目(包含依赖解析、编译、打包)。
|
||||
* <p>
|
||||
* 该命令会依次执行依赖解析、源码编译和产物打包阶段。
|
||||
* </p>
|
||||
*
|
||||
* <pre>
|
||||
* 用法示例:
|
||||
* $ snow build
|
||||
* </pre>
|
||||
*/
|
||||
public final class BuildCommand implements CLICommand {
|
||||
|
||||
/**
|
||||
* 返回命令名称,用于 CLI 调用。
|
||||
*
|
||||
* @return 命令名称,如 "build"
|
||||
*/
|
||||
@Override
|
||||
public String name() {
|
||||
return "build";
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回命令简介,用于 CLI 帮助或命令列表展示。
|
||||
*
|
||||
* @return 命令描述字符串
|
||||
*/
|
||||
@Override
|
||||
public String description() {
|
||||
return "Build the current project by resolving dependencies, compiling, and packaging in sequence.";
|
||||
}
|
||||
|
||||
/**
|
||||
* 打印命令用法信息。
|
||||
*/
|
||||
@Override
|
||||
public void printUsage() {
|
||||
System.out.println("Usage: snow build ");
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行项目构建流程。
|
||||
* <ul>
|
||||
* <li>解析项目描述文件(project.cloud)</li>
|
||||
* <li>依赖解析(RESOLVE_DEPENDENCIES)</li>
|
||||
* <li>源码编译(COMPILE)</li>
|
||||
* <li>产物打包(PACKAGE)</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param args CLI 传入的参数数组
|
||||
* @return 执行结果码(0 表示成功)
|
||||
* @throws Exception 执行过程中出现错误时抛出
|
||||
*/
|
||||
@Override
|
||||
public int execute(String[] args) throws Exception {
|
||||
|
||||
Path dslFile = Paths.get("project.cloud");
|
||||
Project project = CloudDSLParser.parse(dslFile);
|
||||
DependencyResolver resolver = new DependencyResolver(Paths.get(System.getProperty("user.home"), ".snow", "cache"));
|
||||
LifecycleManager lm = new LifecycleManager();
|
||||
|
||||
// 注册各阶段任务
|
||||
lm.register(LifecyclePhase.RESOLVE_DEPENDENCIES, () -> resolver.resolve(project));
|
||||
lm.register(LifecyclePhase.COMPILE, new CompileTask(project));
|
||||
lm.register(LifecyclePhase.PACKAGE, new PackageTask(project));
|
||||
|
||||
lm.executeAll();
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
63
src/main/java/org/jcnc/snow/cli/commands/CleanCommand.java
Normal file
63
src/main/java/org/jcnc/snow/cli/commands/CleanCommand.java
Normal file
@ -0,0 +1,63 @@
|
||||
package org.jcnc.snow.cli.commands;
|
||||
|
||||
import org.jcnc.snow.cli.CLICommand;
|
||||
import org.jcnc.snow.pkg.lifecycle.LifecycleManager;
|
||||
import org.jcnc.snow.pkg.lifecycle.LifecyclePhase;
|
||||
import org.jcnc.snow.pkg.tasks.CleanTask;
|
||||
|
||||
/**
|
||||
* CLI 命令:清理构建输出和本地缓存目录。
|
||||
* <p>
|
||||
* 用于清除项目生成的 build、dist 等中间产物,保持工作区整洁。
|
||||
* </p>
|
||||
*
|
||||
* <pre>
|
||||
* 用法示例:
|
||||
* $ snow clean
|
||||
* </pre>
|
||||
*/
|
||||
public final class CleanCommand implements CLICommand {
|
||||
|
||||
/**
|
||||
* 返回命令名称,用于 CLI 调用。
|
||||
*
|
||||
* @return 命令名称,如 "clean"
|
||||
*/
|
||||
@Override
|
||||
public String name() {
|
||||
return "clean";
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回命令简介,用于 CLI 帮助或命令列表展示。
|
||||
*
|
||||
* @return 命令描述字符串
|
||||
*/
|
||||
@Override
|
||||
public String description() {
|
||||
return "Clean build outputs and local cache, remove intermediate artifacts, and free disk space.";
|
||||
}
|
||||
|
||||
/**
|
||||
* 打印命令用法信息。
|
||||
*/
|
||||
@Override
|
||||
public void printUsage() {
|
||||
System.out.println("Usage: snow clean ");
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行清理任务。
|
||||
*
|
||||
* @param args CLI 传入的参数数组
|
||||
* @return 执行结果码(0 表示成功)
|
||||
* @throws Exception 执行过程中出现错误时抛出
|
||||
*/
|
||||
@Override
|
||||
public int execute(String[] args) throws Exception {
|
||||
LifecycleManager lm = new LifecycleManager();
|
||||
lm.register(LifecyclePhase.CLEAN, new CleanTask());
|
||||
lm.executeAll();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
257
src/main/java/org/jcnc/snow/cli/commands/CompileCommand.java
Normal file
257
src/main/java/org/jcnc/snow/cli/commands/CompileCommand.java
Normal file
@ -0,0 +1,257 @@
|
||||
package org.jcnc.snow.cli.commands;
|
||||
|
||||
import org.jcnc.snow.compiler.backend.alloc.RegisterAllocator;
|
||||
import org.jcnc.snow.compiler.backend.builder.VMCodeGenerator;
|
||||
import org.jcnc.snow.compiler.backend.builder.VMProgramBuilder;
|
||||
import org.jcnc.snow.compiler.backend.core.InstructionGenerator;
|
||||
import org.jcnc.snow.compiler.backend.generator.InstructionGeneratorProvider;
|
||||
import org.jcnc.snow.cli.CLICommand;
|
||||
import org.jcnc.snow.compiler.ir.builder.IRProgramBuilder;
|
||||
import org.jcnc.snow.compiler.ir.core.IRFunction;
|
||||
import org.jcnc.snow.compiler.ir.core.IRInstruction;
|
||||
import org.jcnc.snow.compiler.ir.core.IRProgram;
|
||||
import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
|
||||
import org.jcnc.snow.compiler.lexer.core.LexerEngine;
|
||||
import org.jcnc.snow.compiler.parser.ast.base.Node;
|
||||
import org.jcnc.snow.compiler.parser.context.ParserContext;
|
||||
import org.jcnc.snow.compiler.parser.core.ParserEngine;
|
||||
import org.jcnc.snow.compiler.parser.function.ASTPrinter;
|
||||
import org.jcnc.snow.compiler.semantic.core.SemanticAnalyzerRunner;
|
||||
import org.jcnc.snow.vm.VMLauncher;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
|
||||
|
||||
/**
|
||||
* CLI 命令:将 .snow 源文件编译为 VM 字节码(.water 文件)。
|
||||
* <p>
|
||||
* 支持递归目录、多文件编译,可选编译后立即运行。<br>
|
||||
* 命令参数支持 run、-o、-d 等。
|
||||
* </p>
|
||||
*
|
||||
* <pre>
|
||||
* 用法示例:
|
||||
* $ snow compile [run] [-o <name>] [-d <srcDir>] [file1.snow file2.snow …]
|
||||
* </pre>
|
||||
*/
|
||||
public final class CompileCommand implements CLICommand {
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
/* CLICommand 接口实现 */
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return "compile";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
return "Compile .snow source files into VM byte-code (.water).";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void printUsage() {
|
||||
System.out.println("Usage:");
|
||||
System.out.println(" snow compile [run] [-o <name>] [-d <srcDir>] [file1.snow file2.snow …]");
|
||||
System.out.println("Options:");
|
||||
System.out.println(" run compile then run");
|
||||
System.out.println(" -o <name> specify output base name (without .water suffix)");
|
||||
System.out.println(" -d <srcDir> recursively compile all .snow files in directory");
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
/* 核心:执行 compile 子命令 */
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
@Override
|
||||
public int execute(String[] args) throws Exception {
|
||||
/* ---------------- 解析命令行参数 ---------------- */
|
||||
boolean runAfterCompile = false;
|
||||
String outputName = null;
|
||||
Path dir = null;
|
||||
List<Path> sources = new ArrayList<>();
|
||||
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
String arg = args[i];
|
||||
switch (arg) {
|
||||
case "run" -> runAfterCompile = true;
|
||||
case "-o" -> {
|
||||
if (i + 1 < args.length) outputName = args[++i];
|
||||
else {
|
||||
System.err.println("Missing argument for -o");
|
||||
printUsage();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
case "-d" -> {
|
||||
if (i + 1 < args.length) dir = Path.of(args[++i]);
|
||||
else {
|
||||
System.err.println("Missing argument for -d");
|
||||
printUsage();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
default -> {
|
||||
if (arg.endsWith(".snow")) {
|
||||
sources.add(Path.of(arg));
|
||||
} else {
|
||||
System.err.println("Unknown option or file: " + arg);
|
||||
printUsage();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* --------- 如果指定了目录则递归收集所有 *.snow --------- */
|
||||
if (dir != null) {
|
||||
if (!Files.isDirectory(dir)) {
|
||||
System.err.println("Not a directory: " + dir);
|
||||
return 1;
|
||||
}
|
||||
try (var stream = Files.walk(dir)) {
|
||||
stream.filter(p -> p.toString().endsWith(".snow"))
|
||||
.sorted() // 确保稳定顺序
|
||||
.forEach(sources::add);
|
||||
}
|
||||
}
|
||||
|
||||
if (sources.isEmpty()) {
|
||||
System.err.println("No .snow source files found.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* 多文件但未指定 -o 且非目录编译 —— 提示必须指定输出名 */
|
||||
if (sources.size() > 1 && outputName == null && dir == null) {
|
||||
System.err.println("Please specify output name using -o <name>");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------- */
|
||||
/* 1. 词法 + 语法分析;同时打印源代码 */
|
||||
/* ----------------------------------------------------------------- */
|
||||
List<Node> allAst = new ArrayList<>();
|
||||
|
||||
System.out.println("## 编译器输出");
|
||||
System.out.println("### Snow 源代码"); // ========== 新增:二级标题 ==========
|
||||
|
||||
for (Path p : sources) {
|
||||
if (!Files.exists(p)) {
|
||||
System.err.println("File not found: " + p);
|
||||
return 1;
|
||||
}
|
||||
|
||||
String code = Files.readString(p, StandardCharsets.UTF_8);
|
||||
|
||||
// ------- 打印每个文件的源码 -------
|
||||
System.out.println("#### " + p.getFileName());
|
||||
System.out.println(code);
|
||||
// --------------------------------------------------------
|
||||
|
||||
/* 词法 + 语法 */
|
||||
LexerEngine lexer = new LexerEngine(code, p.toString());
|
||||
ParserContext ctx = new ParserContext(lexer.getAllTokens(), p.toString());
|
||||
allAst.addAll(new ParserEngine(ctx).parse());
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------- */
|
||||
/* 2. 语义分析 */
|
||||
/* ----------------------------------------------------------------- */
|
||||
SemanticAnalyzerRunner.runSemanticAnalysis(allAst, false);
|
||||
|
||||
/* ----------------------------------------------------------------- */
|
||||
/* 3. AST → IR,并把 main 函数调到首位 */
|
||||
/* ----------------------------------------------------------------- */
|
||||
IRProgram program = new IRProgramBuilder().buildProgram(allAst);
|
||||
program = reorderForEntry(program);
|
||||
|
||||
/* ---------------- 打印 AST / IR ---------------- */
|
||||
System.out.println("### AST");
|
||||
ASTPrinter.printJson(allAst);
|
||||
|
||||
System.out.println("### IR");
|
||||
System.out.println(program);
|
||||
|
||||
/* ----------------------------------------------------------------- */
|
||||
/* 4. IR → VM 指令 */
|
||||
/* ----------------------------------------------------------------- */
|
||||
VMProgramBuilder builder = new VMProgramBuilder();
|
||||
List<InstructionGenerator<? extends IRInstruction>> generators =
|
||||
InstructionGeneratorProvider.defaultGenerators();
|
||||
|
||||
for (IRFunction fn : program.functions()) {
|
||||
Map<IRVirtualRegister, Integer> slotMap =
|
||||
new RegisterAllocator().allocate(fn);
|
||||
new VMCodeGenerator(slotMap, builder, generators).generate(fn);
|
||||
}
|
||||
List<String> finalCode = builder.build();
|
||||
|
||||
System.out.println("### VM code");
|
||||
finalCode.forEach(System.out::println);
|
||||
|
||||
/* ----------------------------------------------------------------- */
|
||||
/* 5. 写出 .water 文件 */
|
||||
/* ----------------------------------------------------------------- */
|
||||
Path outputFile = deriveOutputPath(sources, outputName, dir);
|
||||
Files.write(outputFile, finalCode, StandardCharsets.UTF_8);
|
||||
System.out.println("Written to " + outputFile.toAbsolutePath());
|
||||
|
||||
/* ----------------------------------------------------------------- */
|
||||
/* 6. 可选:立即运行 VM */
|
||||
/* ----------------------------------------------------------------- */
|
||||
if (runAfterCompile) {
|
||||
System.out.println("\n=== Launching VM ===");
|
||||
VMLauncher.main(new String[]{outputFile.toString()});
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
/* 辅助方法 */
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* 根据输入情况推断 .water 输出文件名:
|
||||
* <ul>
|
||||
* <li>若指定 -o,则直接使用</li>
|
||||
* <li>目录编译:取目录名</li>
|
||||
* <li>单文件编译:取文件名去掉 .snow</li>
|
||||
* <li>其他情况兜底为 "program"</li>
|
||||
* </ul>
|
||||
*/
|
||||
private static Path deriveOutputPath(List<Path> sources, String outName, Path dir) {
|
||||
String base;
|
||||
if (outName != null) {
|
||||
base = outName;
|
||||
} else if (dir != null) {
|
||||
base = dir.getFileName().toString();
|
||||
} else if (sources.size() == 1) {
|
||||
base = sources.getFirst().getFileName().toString()
|
||||
.replaceFirst("\\.snow$", "");
|
||||
} else {
|
||||
base = "program";
|
||||
}
|
||||
return Path.of(base + ".water");
|
||||
}
|
||||
|
||||
/**
|
||||
* 把 main 函数交换到程序函数列表首位,确保 PC=0 即入口。
|
||||
*/
|
||||
private static IRProgram reorderForEntry(IRProgram in) {
|
||||
List<IRFunction> ordered = new ArrayList<>(in.functions());
|
||||
for (int i = 0; i < ordered.size(); i++) {
|
||||
if ("main".equals(ordered.get(i).name())) {
|
||||
Collections.swap(ordered, 0, i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
IRProgram out = new IRProgram();
|
||||
ordered.forEach(out::add);
|
||||
return out;
|
||||
}
|
||||
}
|
||||
78
src/main/java/org/jcnc/snow/cli/commands/InitCommand.java
Normal file
78
src/main/java/org/jcnc/snow/cli/commands/InitCommand.java
Normal file
@ -0,0 +1,78 @@
|
||||
package org.jcnc.snow.cli.commands;
|
||||
|
||||
import org.jcnc.snow.cli.CLICommand;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
/**
|
||||
* CLI 命令:创建新的项目骨架。
|
||||
* <p>
|
||||
* 用于快速初始化标准目录结构和 DSL 配置文件(project.cloud)。
|
||||
* </p>
|
||||
*
|
||||
* <pre>
|
||||
* 用法示例:
|
||||
* $ snow init
|
||||
* </pre>
|
||||
*/
|
||||
public final class InitCommand implements CLICommand {
|
||||
|
||||
/**
|
||||
* 返回命令名称,用于 CLI 调用。
|
||||
*
|
||||
* @return 命令名称,如 "init"
|
||||
*/
|
||||
@Override
|
||||
public String name() {
|
||||
return "init";
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回命令简介,用于 CLI 帮助或命令列表展示。
|
||||
*
|
||||
* @return 命令描述字符串
|
||||
*/
|
||||
@Override
|
||||
public String description() {
|
||||
return "Initialize a new project skeleton with directory structure and project.cloud file.";
|
||||
}
|
||||
|
||||
/**
|
||||
* 打印命令用法信息。
|
||||
*/
|
||||
@Override
|
||||
public void printUsage() {
|
||||
System.out.println("Usage: snow init [--lang <lang>]");
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行项目初始化流程,创建 src 目录和 DSL 配置文件。
|
||||
*
|
||||
* @param args CLI 传入的参数数组
|
||||
* @return 执行结果码(0 表示成功)
|
||||
* @throws Exception 文件创建过程中出现错误时抛出
|
||||
*/
|
||||
@Override
|
||||
public int execute(String[] args) throws Exception {
|
||||
// 生成 skeleton `.cloud` 文件和 src 目录
|
||||
Path dir = Paths.get(".").toAbsolutePath();
|
||||
Files.createDirectories(dir.resolve("src"));
|
||||
Path dsl = dir.resolve("project.cloud");
|
||||
if (Files.notExists(dsl)) {
|
||||
Files.writeString(dsl, """
|
||||
# Generated by snow init
|
||||
project {
|
||||
group = "com.example"
|
||||
artifact = "demo-app"
|
||||
version = "0.0.1-SNAPSHOT"
|
||||
}
|
||||
""");
|
||||
System.out.println("[init] created " + dsl);
|
||||
} else {
|
||||
System.out.println("[init] project.cloud already exists");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
65
src/main/java/org/jcnc/snow/cli/commands/InstallCommand.java
Normal file
65
src/main/java/org/jcnc/snow/cli/commands/InstallCommand.java
Normal file
@ -0,0 +1,65 @@
|
||||
package org.jcnc.snow.cli.commands;
|
||||
|
||||
import org.jcnc.snow.cli.CLICommand;
|
||||
import org.jcnc.snow.pkg.dsl.CloudDSLParser;
|
||||
import org.jcnc.snow.pkg.model.Project;
|
||||
import org.jcnc.snow.pkg.resolver.DependencyResolver;
|
||||
|
||||
import java.nio.file.Paths;
|
||||
|
||||
/**
|
||||
* CLI 命令:解析并下载项目依赖到本地缓存。
|
||||
* <p>
|
||||
* 适用于离线使用和依赖预热场景,会自动读取项目描述文件并处理依赖缓存。
|
||||
* </p>
|
||||
*
|
||||
* <pre>
|
||||
* 用法示例:
|
||||
* $ snow install
|
||||
* </pre>
|
||||
*/
|
||||
public final class InstallCommand implements CLICommand {
|
||||
|
||||
/**
|
||||
* 返回命令名称,用于 CLI 调用。
|
||||
*
|
||||
* @return 命令名称,如 "install"
|
||||
*/
|
||||
@Override
|
||||
public String name() {
|
||||
return "install";
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回命令简介,用于 CLI 帮助或命令列表展示。
|
||||
*
|
||||
* @return 命令描述字符串
|
||||
*/
|
||||
@Override
|
||||
public String description() {
|
||||
return "Resolve and download project dependencies to local cache for offline development or faster builds.";
|
||||
}
|
||||
|
||||
/**
|
||||
* 打印命令用法信息。
|
||||
*/
|
||||
@Override
|
||||
public void printUsage() {
|
||||
System.out.println("Usage: snow install ");
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行依赖解析和下载任务。
|
||||
*
|
||||
* @param args CLI 传入的参数数组
|
||||
* @return 执行结果码(0 表示成功)
|
||||
* @throws Exception 解析或下载依赖过程中出现错误时抛出
|
||||
*/
|
||||
@Override
|
||||
public int execute(String[] args) throws Exception {
|
||||
Project project = CloudDSLParser.parse(Paths.get("project.cloud"));
|
||||
DependencyResolver resolver = new DependencyResolver(Paths.get(System.getProperty("user.home"), ".snow", "cache"));
|
||||
resolver.resolve(project);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
74
src/main/java/org/jcnc/snow/cli/commands/PublishCommand.java
Normal file
74
src/main/java/org/jcnc/snow/cli/commands/PublishCommand.java
Normal file
@ -0,0 +1,74 @@
|
||||
package org.jcnc.snow.cli.commands;
|
||||
|
||||
import org.jcnc.snow.cli.CLICommand;
|
||||
import org.jcnc.snow.pkg.dsl.CloudDSLParser;
|
||||
import org.jcnc.snow.pkg.lifecycle.LifecycleManager;
|
||||
import org.jcnc.snow.pkg.lifecycle.LifecyclePhase;
|
||||
import org.jcnc.snow.pkg.model.Project;
|
||||
import org.jcnc.snow.pkg.tasks.PublishTask;
|
||||
|
||||
import java.nio.file.Paths;
|
||||
|
||||
/**
|
||||
* CLI 命令:将已构建的项目包发布到远程仓库。
|
||||
* <p>
|
||||
* 用于持续集成、交付或分发场景。
|
||||
* 支持自动读取 DSL 项目描述文件,并注册和执行发布生命周期阶段的任务。
|
||||
* </p>
|
||||
*
|
||||
* <pre>
|
||||
* 用法示例:
|
||||
* $ snow publish
|
||||
* </pre>
|
||||
*/
|
||||
public final class PublishCommand implements CLICommand {
|
||||
|
||||
/**
|
||||
* 返回该命令的名称(用于 CLI 调用)。
|
||||
*
|
||||
* @return 命令名称字符串,如 "publish"
|
||||
*/
|
||||
@Override
|
||||
public String name() {
|
||||
return "publish";
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回命令简介,用于 CLI 帮助或命令列表展示。
|
||||
*
|
||||
* @return 命令描述字符串
|
||||
*/
|
||||
@Override
|
||||
public String description() {
|
||||
return "Publish the built package to a remote repository, suitable for continuous integration, delivery, or project distribution.";
|
||||
}
|
||||
|
||||
/**
|
||||
* 打印命令用法信息,供终端用户参考。
|
||||
*/
|
||||
@Override
|
||||
public void printUsage() {
|
||||
System.out.println("Usage: snow publish ");
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行发布命令。
|
||||
* <ul>
|
||||
* <li>解析项目描述文件(如 project.cloud)</li>
|
||||
* <li>注册并执行 PUBLISH 阶段的任务</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param args CLI 传入的参数数组
|
||||
* @return 执行结果码(0表示成功)
|
||||
* @throws Exception 执行过程中出现错误时抛出
|
||||
*/
|
||||
@Override
|
||||
public int execute(String[] args) throws Exception {
|
||||
Project project = CloudDSLParser.parse(Paths.get("project.cloud"));
|
||||
LifecycleManager lm = new LifecycleManager();
|
||||
lm.register(LifecyclePhase.PUBLISH, new PublishTask(project));
|
||||
lm.executeAll();
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
65
src/main/java/org/jcnc/snow/cli/commands/RunCommand.java
Normal file
65
src/main/java/org/jcnc/snow/cli/commands/RunCommand.java
Normal file
@ -0,0 +1,65 @@
|
||||
package org.jcnc.snow.cli.commands;
|
||||
|
||||
import org.jcnc.snow.cli.CLICommand;
|
||||
import org.jcnc.snow.vm.VMLauncher;
|
||||
|
||||
/**
|
||||
* CLI 命令:运行已编译的 VM 字节码文件(.water)。
|
||||
* <p>
|
||||
* 用于执行 VM 程序文件。支持传递额外 VM 参数,实际运行由 {@link VMLauncher#main(String[])} 完成。
|
||||
* </p>
|
||||
*
|
||||
* <pre>
|
||||
* 用法示例:
|
||||
* $ snow run program.water [additional VM options]
|
||||
* </pre>
|
||||
*/
|
||||
public final class RunCommand implements CLICommand {
|
||||
|
||||
/**
|
||||
* 返回命令名,用于 CLI 调用。
|
||||
*
|
||||
* @return 命令名称字符串("run")
|
||||
*/
|
||||
@Override
|
||||
public String name() {
|
||||
return "run";
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回命令简介,用于 CLI 帮助或命令列表展示。
|
||||
*
|
||||
* @return 命令描述字符串
|
||||
*/
|
||||
@Override
|
||||
public String description() {
|
||||
return "Execute compiled VM instructions.";
|
||||
}
|
||||
|
||||
/**
|
||||
* 打印该命令的用法说明。
|
||||
*/
|
||||
@Override
|
||||
public void printUsage() {
|
||||
System.out.println("""
|
||||
Usage: snow run <program.water> [additional VM options]
|
||||
""");
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行 run 命令,运行指定的 VM 字节码文件。
|
||||
*
|
||||
* @param args 剩余参数(不含命令名),第一个应为 .water 文件路径,其后为可选 VM 参数
|
||||
* @return 0 表示执行成功,1 表示参数错误
|
||||
* @throws Exception VM 启动或执行过程中可能抛出的异常
|
||||
*/
|
||||
@Override
|
||||
public int execute(String[] args) throws Exception {
|
||||
if (args.length == 0) {
|
||||
printUsage();
|
||||
return 1;
|
||||
}
|
||||
VMLauncher.main(args);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
59
src/main/java/org/jcnc/snow/cli/commands/VersionCommand.java
Normal file
59
src/main/java/org/jcnc/snow/cli/commands/VersionCommand.java
Normal file
@ -0,0 +1,59 @@
|
||||
package org.jcnc.snow.cli.commands;
|
||||
|
||||
import org.jcnc.snow.cli.CLICommand;
|
||||
import org.jcnc.snow.cli.SnowCLI;
|
||||
|
||||
/**
|
||||
* CLI 子命令:输出当前 Snow 工具的版本号。
|
||||
* <p>
|
||||
* 用于显示当前 CLI 工具版本,便于诊断、升级、兼容性确认等场景。
|
||||
* </p>
|
||||
*
|
||||
* <pre>
|
||||
* 用法示例:
|
||||
* $ snow version
|
||||
* </pre>
|
||||
*/
|
||||
public final class VersionCommand implements CLICommand {
|
||||
|
||||
/**
|
||||
* 返回命令名,用于 CLI 调用。
|
||||
*
|
||||
* @return 命令名称字符串("version")
|
||||
*/
|
||||
@Override
|
||||
public String name() {
|
||||
return "version";
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回命令简介,用于 CLI 帮助或命令列表展示。
|
||||
*
|
||||
* @return 命令描述字符串
|
||||
*/
|
||||
@Override
|
||||
public String description() {
|
||||
return "Print snow version.";
|
||||
}
|
||||
|
||||
/**
|
||||
* 打印该命令的用法说明。
|
||||
*/
|
||||
@Override
|
||||
public void printUsage() {
|
||||
System.out.println("Usage:");
|
||||
System.out.println(" snow version");
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行 version 命令,输出当前 Snow 的版本号。
|
||||
*
|
||||
* @param args 命令参数,此命令不接受额外参数
|
||||
* @return 0 表示成功执行
|
||||
*/
|
||||
@Override
|
||||
public int execute(String[] args) {
|
||||
System.out.println("snow version \"" + SnowCLI.SNOW_VERSION + "\"");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
70
src/main/java/org/jcnc/snow/cli/utils/CLIUtils.java
Normal file
70
src/main/java/org/jcnc/snow/cli/utils/CLIUtils.java
Normal file
@ -0,0 +1,70 @@
|
||||
package org.jcnc.snow.cli.utils;
|
||||
|
||||
import org.jcnc.snow.cli.CLICommand;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* 命令行界面通用工具类,提供全局帮助和版本标志、全局选项定义,以及打印通用用法的方法。
|
||||
*/
|
||||
public class CLIUtils {
|
||||
|
||||
/**
|
||||
* 全局帮助标志集合,支持 "help"、"-h"、"--help"。
|
||||
*/
|
||||
public static final Set<String> GLOBAL_HELP_FLAGS = Set.of(
|
||||
"help", "-h", "--help"
|
||||
);
|
||||
|
||||
/**
|
||||
* 全局版本标志集合,支持 "-v"、"--version"。
|
||||
*/
|
||||
public static final Set<String> GLOBAL_VERSION_FLAGS = Set.of(
|
||||
"-v", "--version"
|
||||
);
|
||||
|
||||
/**
|
||||
* 全局选项列表,包括帮助和版本选项的描述。
|
||||
*/
|
||||
public static final List<Option> GLOBAL_OPTIONS = List.of(
|
||||
new Option(List.of("-h", "--help"), "Show this help message and exit"),
|
||||
new Option(List.of("-v", "--version"), "Print snow programming language version and exit")
|
||||
);
|
||||
|
||||
/**
|
||||
* 打印命令行工具的通用用法说明。
|
||||
* <p>
|
||||
*
|
||||
* @param commands 可用子命令名称到命令处理器的映射,用于列出所有子命令及其描述
|
||||
*/
|
||||
public static void printGeneralUsage(Map<String, Supplier<CLICommand>> commands) {
|
||||
System.out.println("Usage:");
|
||||
System.out.println(" snow [OPTIONS] <command>");
|
||||
System.out.println();
|
||||
System.out.println("Options:");
|
||||
for (Option opt : GLOBAL_OPTIONS) {
|
||||
String flags = String.join(", ", opt.flags());
|
||||
System.out.printf(" %-15s %s%n", flags, opt.description());
|
||||
}
|
||||
System.out.println();
|
||||
System.out.println("Commands:");
|
||||
commands.forEach((name, supplier) -> {
|
||||
CLICommand c = supplier.get();
|
||||
System.out.printf(" %-10s %s%n", name, c.description());
|
||||
});
|
||||
System.out.println();
|
||||
System.out.println("Use \"snow <command> --help\" for command-specific options.");
|
||||
}
|
||||
|
||||
/**
|
||||
* 全局选项的数据结构,包含选项标志和描述。
|
||||
*
|
||||
* @param flags 选项标志列表,例如 ["-h", "--help"]
|
||||
* @param description 选项功能描述
|
||||
*/
|
||||
public record Option(List<String> flags, String description) {
|
||||
}
|
||||
}
|
||||
41
src/main/java/org/jcnc/snow/cli/utils/VersionUtils.java
Normal file
41
src/main/java/org/jcnc/snow/cli/utils/VersionUtils.java
Normal file
@ -0,0 +1,41 @@
|
||||
package org.jcnc.snow.cli.utils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* 版本工具类,用于加载资源文件中的 Snow 编程语言版本信息。
|
||||
* <p>
|
||||
* 从 classpath 路径下的 version.properties 文件中读取属性 "snow.version",
|
||||
* 并返回对应的版本号字符串。
|
||||
* </p>
|
||||
*/
|
||||
public class VersionUtils {
|
||||
|
||||
/**
|
||||
* 资源文件路径常量,指向 classpath 根目录下的 version.properties 文件。
|
||||
*/
|
||||
private static final String PROPERTIES_PATH = "/version.properties";
|
||||
|
||||
/**
|
||||
* 加载并返回 Snow 编程语言的版本号。
|
||||
* <p>
|
||||
* 通过 Class.getResourceAsStream 方法读取 version.properties 文件,
|
||||
* 使用 {@link Properties} 类解析并获取 key 为 "snow.version" 的值。
|
||||
* </p>
|
||||
*
|
||||
* @return 从 version.properties 中读取的版本号(如 "1.0.0")
|
||||
* @throws UncheckedIOException 如果读取或解析配置文件时发生 I/O 错误
|
||||
*/
|
||||
public static String loadVersion() {
|
||||
try (InputStream in = VersionUtils.class.getResourceAsStream(PROPERTIES_PATH)) {
|
||||
Properties properties = new Properties();
|
||||
properties.load(in);
|
||||
return properties.getProperty("snow.version");
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException("Failed to load version from " + PROPERTIES_PATH, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,42 @@
|
||||
package org.jcnc.snow.compiler.backend.generator;
|
||||
|
||||
import org.jcnc.snow.compiler.backend.core.InstructionGenerator;
|
||||
import org.jcnc.snow.compiler.ir.core.IRInstruction;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 指令生成器集中注册表。
|
||||
*
|
||||
* <p>本类在类加载阶段即完成所有后端 {@link InstructionGenerator} 实例
|
||||
* 的创建,并以不可变列表形式对外暴露。
|
||||
*/
|
||||
public final class InstructionGeneratorProvider {
|
||||
|
||||
/** 工具类禁止实例化。 */
|
||||
private InstructionGeneratorProvider() { /* no-instance */ }
|
||||
|
||||
/** 缺省指令生成器列表(不可修改,顺序即执行顺序)。 */
|
||||
private static final List<InstructionGenerator<? extends IRInstruction>> DEFAULT =
|
||||
List.of(
|
||||
new LoadConstGenerator(), // 常量加载
|
||||
new BinaryOpGenerator(), // 二元运算
|
||||
new UnaryOpGenerator(), // 一元运算
|
||||
new CallGenerator(), // 函数调用
|
||||
new ReturnGenerator(), // 函数返回
|
||||
new LabelGenerator(), // 标签定义
|
||||
new JumpGenerator(), // 无条件跳转
|
||||
new CmpJumpGenerator() // 条件跳转
|
||||
);
|
||||
|
||||
/**
|
||||
* 返回生产环境使用的缺省指令生成器列表。
|
||||
* 该列表为不可变集合,如尝试修改将抛出
|
||||
* {@link UnsupportedOperationException}。
|
||||
*
|
||||
* @return 不可变的 {@code List} 实例
|
||||
*/
|
||||
public static List<InstructionGenerator<? extends IRInstruction>> defaultGenerators() {
|
||||
return DEFAULT;
|
||||
}
|
||||
}
|
||||
@ -1,149 +0,0 @@
|
||||
package org.jcnc.snow.compiler.cli;
|
||||
|
||||
import org.jcnc.snow.compiler.backend.alloc.RegisterAllocator;
|
||||
import org.jcnc.snow.compiler.backend.builder.VMCodeGenerator;
|
||||
import org.jcnc.snow.compiler.backend.builder.VMProgramBuilder;
|
||||
import org.jcnc.snow.compiler.backend.core.InstructionGenerator;
|
||||
import org.jcnc.snow.compiler.backend.generator.*;
|
||||
import org.jcnc.snow.compiler.ir.builder.IRProgramBuilder;
|
||||
import org.jcnc.snow.compiler.ir.core.IRFunction;
|
||||
import org.jcnc.snow.compiler.ir.core.IRInstruction;
|
||||
import org.jcnc.snow.compiler.ir.core.IRProgram;
|
||||
import org.jcnc.snow.compiler.lexer.core.LexerEngine;
|
||||
import org.jcnc.snow.compiler.parser.ast.base.Node;
|
||||
import org.jcnc.snow.compiler.parser.context.ParserContext;
|
||||
import org.jcnc.snow.compiler.parser.core.ParserEngine;
|
||||
import org.jcnc.snow.compiler.parser.function.ASTPrinter;
|
||||
import org.jcnc.snow.compiler.semantic.core.SemanticAnalyzerRunner;
|
||||
import org.jcnc.snow.vm.engine.VMMode;
|
||||
import org.jcnc.snow.vm.engine.VirtualMachineEngine;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.*;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* SnowCompiler CLI —— 多文件 / 单文件 / 目录 模式。
|
||||
*/
|
||||
public class SnowCompiler {
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
if (args.length == 0) {
|
||||
System.err.println("""
|
||||
Usage:
|
||||
snow <file1.snow> [file2.snow …]
|
||||
snow -d <srcDir> (compile all *.snow recursively)
|
||||
""");
|
||||
return;
|
||||
}
|
||||
|
||||
/* ---------- 1. 收集所有待编译源码 ---------- */
|
||||
List<Path> sources = collectSources(args);
|
||||
if (sources.isEmpty()) {
|
||||
System.err.println("No .snow source files found.");
|
||||
return;
|
||||
}
|
||||
|
||||
/* ---------- 2. 逐个词法+语法分析,合并 AST ---------- */
|
||||
List<Node> allAst = new ArrayList<>();
|
||||
for (Path p : sources) {
|
||||
if (!Files.exists(p)) {
|
||||
System.err.println("File not found: " + p);
|
||||
return;
|
||||
}
|
||||
String code = Files.readString(p, StandardCharsets.UTF_8);
|
||||
|
||||
// 保持原有“## 源代码”打印,但标注文件名,兼容旧脚本
|
||||
System.out.println("## 源代码 (" + p.getFileName() + ")");
|
||||
System.out.println(code);
|
||||
|
||||
LexerEngine lexer = new LexerEngine(code, p.toString());
|
||||
ParserContext ctx = new ParserContext(lexer.getAllTokens(), p.toString());
|
||||
allAst.addAll(new ParserEngine(ctx).parse());
|
||||
}
|
||||
|
||||
/* ---------- 3. 语义分析 ---------- */
|
||||
SemanticAnalyzerRunner.runSemanticAnalysis(allAst, false);
|
||||
|
||||
/* ---------- 4. AST → IR ---------- */
|
||||
IRProgram program = new IRProgramBuilder().buildProgram(allAst);
|
||||
program = reorderForEntry(program); // 保证入口 main 在首位
|
||||
|
||||
System.out.println("## 编译器输出");
|
||||
System.out.println("### AST");
|
||||
ASTPrinter.printJson(allAst);
|
||||
System.out.println("### IR");
|
||||
System.out.println(program);
|
||||
|
||||
/* ---------- 5. IR → VM 指令 ---------- */
|
||||
VMProgramBuilder builder = new VMProgramBuilder();
|
||||
List<InstructionGenerator<? extends IRInstruction>> generators = Arrays.asList(
|
||||
new LoadConstGenerator(),
|
||||
new BinaryOpGenerator(),
|
||||
new UnaryOpGenerator(),
|
||||
new CallGenerator(),
|
||||
new ReturnGenerator(),
|
||||
new LabelGenerator(),
|
||||
new JumpGenerator(),
|
||||
new CmpJumpGenerator()
|
||||
);
|
||||
|
||||
for (IRFunction fn : program.functions()) {
|
||||
Map<org.jcnc.snow.compiler.ir.value.IRVirtualRegister, Integer> slotMap =
|
||||
new RegisterAllocator().allocate(fn);
|
||||
new VMCodeGenerator(slotMap, builder, generators).generate(fn);
|
||||
}
|
||||
List<String> finalCode = builder.build();
|
||||
|
||||
System.out.println("### VM code");
|
||||
finalCode.forEach(System.out::println);
|
||||
|
||||
/* ---------- 6. 运行虚拟机 ---------- */
|
||||
VirtualMachineEngine vm = new VirtualMachineEngine(VMMode.RUN);
|
||||
vm.execute(finalCode);
|
||||
vm.printLocalVariables();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 根据参数收集待编译文件:
|
||||
* - snow file1 file2 … ← 多文件 / 单文件
|
||||
* - snow -d srcDir ← 目录递归所有 *.snow
|
||||
*/
|
||||
private static List<Path> collectSources(String[] args) throws IOException {
|
||||
if (args.length == 2 && "-d".equals(args[0])) {
|
||||
Path dir = Path.of(args[1]);
|
||||
if (!Files.isDirectory(dir)) {
|
||||
System.err.println("Not a directory: " + dir);
|
||||
return List.of();
|
||||
}
|
||||
try (var stream = Files.walk(dir)) {
|
||||
return stream.filter(p -> p.toString().endsWith(".snow"))
|
||||
.sorted() // 稳定顺序,方便比对输出
|
||||
.toList();
|
||||
}
|
||||
}
|
||||
// 普通文件参数
|
||||
return Arrays.stream(args).map(Path::of).toList();
|
||||
}
|
||||
|
||||
/**
|
||||
* 把 main 函数放到 Program.functions()[0],保证 PC=0 即入口;
|
||||
* 如果用户未写 main,则保持原顺序(语义分析会报错)。
|
||||
*/
|
||||
private static IRProgram reorderForEntry(IRProgram in) {
|
||||
List<IRFunction> ordered = new ArrayList<>(in.functions());
|
||||
int idx = -1;
|
||||
for (int i = 0; i < ordered.size(); i++) {
|
||||
if ("main".equals(ordered.get(i).name())) {
|
||||
idx = i; break;
|
||||
}
|
||||
}
|
||||
if (idx > 0) Collections.swap(ordered, 0, idx);
|
||||
|
||||
IRProgram out = new IRProgram();
|
||||
ordered.forEach(out::add);
|
||||
return out;
|
||||
}
|
||||
}
|
||||
125
src/main/java/org/jcnc/snow/pkg/dsl/CloudDSLParser.java
Normal file
125
src/main/java/org/jcnc/snow/pkg/dsl/CloudDSLParser.java
Normal file
@ -0,0 +1,125 @@
|
||||
package org.jcnc.snow.pkg.dsl;
|
||||
|
||||
import org.jcnc.snow.pkg.model.Project;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* CloudDSLParser — .cloud 配置文件解析器
|
||||
* <p>
|
||||
* 作用:
|
||||
* - 读取 Snow 构建工具自定义的 .cloud 文件
|
||||
* - 将内容转换为内存中的 {@link Project} 模型
|
||||
* <p>
|
||||
* 解析规则(:
|
||||
* <p>
|
||||
* 1. 顶级区块(project、properties、repositories、dependencies、build)
|
||||
* 以 “sectionName {” 开始,以 “}” 结束。
|
||||
* <p>
|
||||
* 2. 区块内部识别 “key = value” 形式的赋值。
|
||||
* <p>
|
||||
* 3. build 区块允许嵌套;内部键通过 “.” 展平,
|
||||
* 例如 compile.enabled = true。
|
||||
* <p>
|
||||
*/
|
||||
public final class CloudDSLParser {
|
||||
|
||||
/* ---------- 正则表达式 ---------- */
|
||||
|
||||
/**
|
||||
* 匹配 “sectionName {” 形式的行
|
||||
*/
|
||||
private static final Pattern SECTION_HEADER = Pattern.compile("^(\\w+)\\s*\\{\\s*$");
|
||||
|
||||
/**
|
||||
* 匹配 “key = value” 行。
|
||||
* value 允许空格,并忽略行尾 “# …” 注释。
|
||||
*/
|
||||
private static final Pattern KEY_VALUE = Pattern.compile("^(\\w+)\\s*=\\s*([^#]+?)\\s*(?:#.*)?$");
|
||||
|
||||
/**
|
||||
* 匹配仅包含 “}” 的行(可有前后空白)
|
||||
*/
|
||||
private static final Pattern BLOCK_END = Pattern.compile("^}\\s*$");
|
||||
|
||||
/**
|
||||
* 私有构造,禁止实例化
|
||||
*/
|
||||
private CloudDSLParser() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析指定 .cloud 文件并生成 {@link Project} 对象。
|
||||
*
|
||||
* @param path 文件路径
|
||||
* @return 解析后的 Project
|
||||
* @throws IOException 文件读取失败
|
||||
* @throws IllegalStateException 语法错误(如括号不匹配、未知语句)
|
||||
*/
|
||||
public static Project parse(Path path) throws IOException {
|
||||
|
||||
Deque<String> sectionStack = new ArrayDeque<>(); // 记录当前区块层级
|
||||
Map<String, String> flatMap = new LinkedHashMap<>(); // 扁平化 key → value
|
||||
|
||||
List<String> lines = Files.readAllLines(path);
|
||||
|
||||
int lineNo = 0;
|
||||
for (String raw : lines) {
|
||||
lineNo++;
|
||||
String line = raw.trim();
|
||||
|
||||
/* 1. 跳过空行和注释 */
|
||||
if (line.isEmpty() || line.startsWith("#")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* 2. 区块起始:sectionName { */
|
||||
Matcher sec = SECTION_HEADER.matcher(line);
|
||||
if (sec.matches()) {
|
||||
sectionStack.push(sec.group(1));
|
||||
continue;
|
||||
}
|
||||
|
||||
/* 3. 区块结束:} */
|
||||
if (BLOCK_END.matcher(line).matches()) {
|
||||
if (sectionStack.isEmpty()) {
|
||||
throw new IllegalStateException("第 " + lineNo + " 行出现未配对的 '}'");
|
||||
}
|
||||
sectionStack.pop();
|
||||
continue;
|
||||
}
|
||||
|
||||
/* 4. 键值对:key = value */
|
||||
Matcher kv = KEY_VALUE.matcher(line);
|
||||
if (kv.matches()) {
|
||||
String key = kv.group(1).trim();
|
||||
String value = kv.group(2).trim();
|
||||
|
||||
/* 4.1 计算前缀(栈倒序,即从外到内) */
|
||||
String prefix = String.join(".", (Iterable<String>) sectionStack::descendingIterator);
|
||||
if (!prefix.isEmpty()) {
|
||||
key = prefix + "." + key;
|
||||
}
|
||||
|
||||
flatMap.put(key, value);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* 5. 无法识别的行 */
|
||||
throw new IllegalStateException("无法解析第 " + lineNo + " 行: " + raw);
|
||||
}
|
||||
|
||||
/* 6. 检查括号是否全部闭合 */
|
||||
if (!sectionStack.isEmpty()) {
|
||||
throw new IllegalStateException("文件结束但区块未闭合:" + sectionStack);
|
||||
}
|
||||
|
||||
/* 7. 构造 Project 模型 */
|
||||
return Project.fromFlatMap(flatMap);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,63 @@
|
||||
package org.jcnc.snow.pkg.lifecycle;
|
||||
|
||||
import org.jcnc.snow.pkg.tasks.Task;
|
||||
|
||||
import java.util.EnumMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 管理不同生命周期阶段与其对应任务的工具类。
|
||||
* <p>
|
||||
* 可为每个 {@link LifecyclePhase} 注册对应的 {@link Task},并按阶段顺序执行所有任务。
|
||||
* </p>
|
||||
*
|
||||
* <pre>
|
||||
* 示例用法:
|
||||
* LifecycleManager manager = new LifecycleManager();
|
||||
* manager.register(LifecyclePhase.INIT, new InitTask());
|
||||
* manager.executeAll();
|
||||
* </pre>
|
||||
*/
|
||||
public final class LifecycleManager {
|
||||
|
||||
/**
|
||||
* 存储生命周期阶段与对应任务的映射关系。
|
||||
*/
|
||||
private final Map<LifecyclePhase, Task> tasks = new EnumMap<>(LifecyclePhase.class);
|
||||
|
||||
/**
|
||||
* 为指定生命周期阶段注册任务。
|
||||
* <p>若该阶段已有任务,则会被新任务覆盖。</p>
|
||||
*
|
||||
* @param phase 生命周期阶段,不能为空
|
||||
* @param task 对应任务,不能为空
|
||||
* @throws NullPointerException 若 phase 或 task 为 null
|
||||
*/
|
||||
public void register(LifecyclePhase phase, Task task) {
|
||||
if (phase == null) {
|
||||
throw new NullPointerException("Lifecycle phase must not be null");
|
||||
}
|
||||
if (task == null) {
|
||||
throw new NullPointerException("Task must not be null");
|
||||
}
|
||||
tasks.put(phase, task);
|
||||
}
|
||||
|
||||
/**
|
||||
* 按 {@link LifecyclePhase} 声明顺序依次执行所有已注册任务。
|
||||
* <p>
|
||||
* 未注册任务的阶段将被跳过。任务执行前会打印阶段名。
|
||||
* </p>
|
||||
*
|
||||
* @throws Exception 若某个任务执行时抛出异常,将直接抛出并终止后续任务执行
|
||||
*/
|
||||
public void executeAll() throws Exception {
|
||||
for (LifecyclePhase phase : LifecyclePhase.values()) {
|
||||
Task task = tasks.get(phase);
|
||||
if (task != null) {
|
||||
System.out.println(">>> Phase: " + phase);
|
||||
task.run();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,19 @@
|
||||
package org.jcnc.snow.pkg.lifecycle;
|
||||
|
||||
/**
|
||||
* 定义了典型软件包生命周期的各个阶段。
|
||||
*/
|
||||
public enum LifecyclePhase {
|
||||
/** 初始化阶段 */
|
||||
INIT,
|
||||
/** 解析依赖阶段 */
|
||||
RESOLVE_DEPENDENCIES,
|
||||
/** 编译阶段 */
|
||||
COMPILE,
|
||||
/** 打包阶段 */
|
||||
PACKAGE,
|
||||
/** 发布阶段 */
|
||||
PUBLISH,
|
||||
/** 清理阶段 */
|
||||
CLEAN
|
||||
}
|
||||
@ -0,0 +1,63 @@
|
||||
package org.jcnc.snow.pkg.model;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* 构建配置对象,封装构建过程中的所有选项。
|
||||
* <p>
|
||||
* 支持基于模板变量(形如{@code @{key}})的选项值替换。
|
||||
* </p>
|
||||
*/
|
||||
public final class BuildConfiguration {
|
||||
|
||||
/** 存储配置项的键值对 */
|
||||
private final Map<String, String> options;
|
||||
|
||||
/**
|
||||
* 私有构造函数,用于初始化配置项。
|
||||
*
|
||||
* @param options 配置项键值对
|
||||
*/
|
||||
private BuildConfiguration(Map<String, String> options) {
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
/**
|
||||
* 基于原始配置项和变量属性创建配置对象。
|
||||
* <p>
|
||||
* 会将原始配置中的所有值中的{@code @{key}}模板,替换为属性props中对应的值。
|
||||
* </p>
|
||||
*
|
||||
* @param flat 原始的配置项,值中可包含模板变量(如@{name})
|
||||
* @param props 用于替换模板变量的属性集
|
||||
* @return 构建完成的配置对象
|
||||
*/
|
||||
public static BuildConfiguration fromFlatMap(Map<String, String> flat, Map<String, String> props) {
|
||||
Map<String, String> resolved = new HashMap<>();
|
||||
for (Map.Entry<String, String> e : flat.entrySet()) {
|
||||
String value = e.getValue();
|
||||
for (Map.Entry<String, String> p : props.entrySet()) {
|
||||
value = value.replace("@{" + p.getKey() + "}", p.getValue());
|
||||
}
|
||||
resolved.put(e.getKey(), value);
|
||||
}
|
||||
return new BuildConfiguration(resolved);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定key对应的配置值。
|
||||
*
|
||||
* @param key 配置项名称
|
||||
* @param def 默认值(若未找到key则返回此值)
|
||||
* @return 配置项对应值,若不存在则返回默认值
|
||||
*/
|
||||
public String get(String key, String def) {
|
||||
return options.getOrDefault(key, def);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return options.toString();
|
||||
}
|
||||
}
|
||||
82
src/main/java/org/jcnc/snow/pkg/model/Dependency.java
Normal file
82
src/main/java/org/jcnc/snow/pkg/model/Dependency.java
Normal file
@ -0,0 +1,82 @@
|
||||
package org.jcnc.snow.pkg.model;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* 表示一个 Maven 风格的 group:artifact:version 依赖坐标。
|
||||
* <p>
|
||||
* 支持通过占位符和属性映射进行动态变量替换,可用于构建工具或依赖管理场景。
|
||||
* </p>
|
||||
*
|
||||
* <pre>
|
||||
* 示例:
|
||||
* Dependency dep = Dependency.fromString(
|
||||
* "core", "com.example:core:@{version}",
|
||||
* Map.of("version", "1.2.3")
|
||||
* );
|
||||
* </pre>
|
||||
*/
|
||||
public record Dependency(
|
||||
String id,
|
||||
String group,
|
||||
String artifact,
|
||||
String version
|
||||
) {
|
||||
|
||||
/**
|
||||
* 用于匹配 group:artifact:version 格式的正则表达式。
|
||||
*/
|
||||
private static final Pattern GAV = Pattern.compile("([^:]+):([^:]+):(.+)");
|
||||
|
||||
/**
|
||||
* 根据字符串坐标和属性映射创建依赖对象。
|
||||
* <p>
|
||||
* 坐标中的占位符如 {@code @{key}} 会用 props 中对应的值替换。
|
||||
* </p>
|
||||
*
|
||||
* @param id 依赖唯一标识
|
||||
* @param coordinate 依赖坐标字符串,格式为 group:artifact:version,支持变量占位符
|
||||
* @param props 占位符替换的属性映射
|
||||
* @return 解析后的 Dependency 实例
|
||||
* @throws IllegalArgumentException 如果坐标格式非法
|
||||
*/
|
||||
public static Dependency fromString(String id, String coordinate, Map<String, String> props) {
|
||||
// 替换 @{prop} 占位符
|
||||
String resolved = coordinate;
|
||||
for (Map.Entry<String, String> p : props.entrySet()) {
|
||||
resolved = resolved.replace("@{" + p.getKey() + "}", p.getValue());
|
||||
}
|
||||
|
||||
Matcher m = GAV.matcher(resolved);
|
||||
if (!m.matches()) {
|
||||
throw new IllegalArgumentException("Invalid dependency format: " + coordinate);
|
||||
}
|
||||
return new Dependency(id, m.group(1), m.group(2), m.group(3));
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成依赖对应的标准仓库 jar 路径。
|
||||
* <p>
|
||||
* 路径格式通常为:groupId/artifactId/version/artifactId-version.jar<br>
|
||||
* 例如:com/example/core/1.2.3/core-1.2.3.jar
|
||||
* </p>
|
||||
*
|
||||
* @return 仓库 jar 文件的相对路径
|
||||
*/
|
||||
public String toPath() {
|
||||
String groupPath = group.replace('.', '/');
|
||||
return groupPath + "/" + artifact + "/" + version + "/" + artifact + "-" + version + ".jar";
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回该依赖的 group:artifact:version 字符串表示。
|
||||
*
|
||||
* @return Maven 坐标字符串
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return group + ":" + artifact + ":" + version;
|
||||
}
|
||||
}
|
||||
193
src/main/java/org/jcnc/snow/pkg/model/Project.java
Normal file
193
src/main/java/org/jcnc/snow/pkg/model/Project.java
Normal file
@ -0,0 +1,193 @@
|
||||
package org.jcnc.snow.pkg.model;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 表示一个软件包/模块的项目信息,包括元数据、属性、仓库、依赖和构建配置等。
|
||||
* <p>
|
||||
* 本类为不可变对象,仅提供getter,无setter。
|
||||
* 支持通过 {@link #fromFlatMap(Map)} 静态工厂方法从扁平Map快速创建。
|
||||
* </p>
|
||||
*
|
||||
* <pre>
|
||||
* Map<String,String> map = ...;
|
||||
* Project project = Project.fromFlatMap(map);
|
||||
* </pre>
|
||||
*/
|
||||
public final class Project {
|
||||
|
||||
/** 组织/分组名(如com.example) */
|
||||
private final String group;
|
||||
/** 构件/模块名(如app-core) */
|
||||
private final String artifact;
|
||||
/** 项目展示名称 */
|
||||
private final String name;
|
||||
/** 版本号(如1.0.0) */
|
||||
private final String version;
|
||||
/** 项目描述 */
|
||||
private final String description;
|
||||
/** 许可证标识 */
|
||||
private final String license;
|
||||
/** 项目主页URL */
|
||||
private final String homepage;
|
||||
|
||||
/** 额外属性(不影响主字段,可用于模板/占位符) */
|
||||
private final Map<String, String> properties;
|
||||
/** 仓库列表(仓库ID -> 仓库对象) */
|
||||
private final Map<String, Repository> repositories;
|
||||
/** 依赖列表 */
|
||||
private final List<Dependency> dependencies;
|
||||
/** 构建配置 */
|
||||
private final BuildConfiguration build;
|
||||
|
||||
|
||||
/**
|
||||
* 构造函数(私有),请使用 {@link #fromFlatMap(Map)} 创建实例。
|
||||
*/
|
||||
private Project(
|
||||
String group,
|
||||
String artifact,
|
||||
String name,
|
||||
String version,
|
||||
String description,
|
||||
String license,
|
||||
String homepage,
|
||||
Map<String, String> properties,
|
||||
Map<String, Repository> repositories,
|
||||
List<Dependency> dependencies,
|
||||
BuildConfiguration build
|
||||
) {
|
||||
this.group = group;
|
||||
this.artifact = artifact;
|
||||
this.name = name;
|
||||
this.version = version;
|
||||
this.description = description;
|
||||
this.license = license;
|
||||
this.homepage = homepage;
|
||||
this.properties = properties;
|
||||
this.repositories = repositories;
|
||||
this.dependencies = dependencies;
|
||||
this.build = build;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过扁平Map创建 Project 实例。约定key格式如下:
|
||||
* <ul>
|
||||
* <li>project.* —— 项目元数据</li>
|
||||
* <li>properties.* —— 额外属性</li>
|
||||
* <li>repositories.* —— 仓库</li>
|
||||
* <li>dependencies.* —— 依赖</li>
|
||||
* <li>build.* —— 构建配置</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param map 扁平的配置map
|
||||
* @return Project 实例
|
||||
*/
|
||||
public static Project fromFlatMap(Map<String, String> map) {
|
||||
|
||||
// 1. simple project metadata
|
||||
String group = map.getOrDefault("project.group", "unknown");
|
||||
String artifact = map.getOrDefault("project.artifact", "unknown");
|
||||
String name = map.getOrDefault("project.name", artifact);
|
||||
String version = map.getOrDefault("project.version", "0.0.1-SNAPSHOT");
|
||||
String description = map.getOrDefault("project.description", "");
|
||||
String license = map.getOrDefault("project.license", "");
|
||||
String homepage = map.getOrDefault("project.homepage", "");
|
||||
|
||||
// 2. properties.*
|
||||
Map<String, String> props = new LinkedHashMap<>();
|
||||
map.forEach((k, v) -> {
|
||||
if (k.startsWith("properties.")) {
|
||||
props.put(k.substring("properties.".length()), v);
|
||||
}
|
||||
});
|
||||
|
||||
// 3. repositories.*
|
||||
Map<String, Repository> repos = new LinkedHashMap<>();
|
||||
map.forEach((k, v) -> {
|
||||
if (k.startsWith("repositories.")) {
|
||||
String id = k.substring("repositories.".length());
|
||||
repos.put(id, new Repository(id, v));
|
||||
}
|
||||
});
|
||||
|
||||
// 4. dependencies.*
|
||||
List<Dependency> deps = new ArrayList<>();
|
||||
map.forEach((k, v) -> {
|
||||
if (k.startsWith("dependencies.")) {
|
||||
String id = k.substring("dependencies.".length());
|
||||
deps.add(Dependency.fromString(id, v, props));
|
||||
}
|
||||
});
|
||||
|
||||
// 5. build.* simply hand the subtree map
|
||||
Map<String, String> buildMap = new LinkedHashMap<>();
|
||||
map.forEach((k, v) -> {
|
||||
if (k.startsWith("build.")) {
|
||||
buildMap.put(k.substring("build.".length()), v);
|
||||
}
|
||||
});
|
||||
|
||||
BuildConfiguration buildCfg = BuildConfiguration.fromFlatMap(buildMap, props);
|
||||
|
||||
return new Project(group, artifact, name, version, description, license, homepage, props, repos, deps, buildCfg);
|
||||
}
|
||||
|
||||
/** @return 组织/分组名 */
|
||||
public String getGroup() {
|
||||
return group;
|
||||
}
|
||||
|
||||
/** @return 构件/模块名 */
|
||||
public String getArtifact() {
|
||||
return artifact;
|
||||
}
|
||||
|
||||
/** @return 项目名称 */
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/** @return 版本号 */
|
||||
public String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
/** @return 项目描述 */
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
/** @return 许可证 */
|
||||
public String getLicense() {
|
||||
return license;
|
||||
}
|
||||
|
||||
/** @return 项目主页URL */
|
||||
public String getHomepage() {
|
||||
return homepage;
|
||||
}
|
||||
|
||||
/** @return 额外属性映射 */
|
||||
public Map<String, String> getProperties() {
|
||||
return properties;
|
||||
}
|
||||
|
||||
/** @return 仓库映射 */
|
||||
public Map<String, Repository> getRepositories() {
|
||||
return repositories;
|
||||
}
|
||||
|
||||
/** @return 依赖列表 */
|
||||
public List<Dependency> getDependencies() {
|
||||
return dependencies;
|
||||
}
|
||||
|
||||
/** @return 构建配置 */
|
||||
public BuildConfiguration getBuild() {
|
||||
return build;
|
||||
}
|
||||
}
|
||||
18
src/main/java/org/jcnc/snow/pkg/model/Repository.java
Normal file
18
src/main/java/org/jcnc/snow/pkg/model/Repository.java
Normal file
@ -0,0 +1,18 @@
|
||||
package org.jcnc.snow.pkg.model;
|
||||
|
||||
/**
|
||||
* 表示一个远程仓库的基本信息,通常用于依赖解析和发布。
|
||||
* <p>
|
||||
* 每个仓库由唯一的ID和对应的URL确定。
|
||||
* </p>
|
||||
*
|
||||
* <pre>
|
||||
* 示例:
|
||||
* Repository repo = new Repository("central", "https://");
|
||||
* </pre>
|
||||
*
|
||||
* @param id 仓库唯一标识
|
||||
* @param url 仓库地址(一般为HTTP(S)链接)
|
||||
*/
|
||||
public record Repository(String id, String url) {
|
||||
}
|
||||
@ -0,0 +1,82 @@
|
||||
package org.jcnc.snow.pkg.resolver;
|
||||
|
||||
import org.jcnc.snow.pkg.model.Dependency;
|
||||
import org.jcnc.snow.pkg.model.Project;
|
||||
import org.jcnc.snow.pkg.model.Repository;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* 负责解析并下载项目依赖的工具类。
|
||||
* <p>
|
||||
* 支持本地缓存,如果依赖已存在于本地缓存则直接使用,否则尝试从项目仓库下载依赖包。
|
||||
* </p>
|
||||
*/
|
||||
public final class DependencyResolver {
|
||||
|
||||
/** 本地缓存目录 */
|
||||
private final Path localCache;
|
||||
|
||||
/**
|
||||
* 创建 DependencyResolver 实例。
|
||||
*
|
||||
* @param localCacheDir 用于缓存依赖的本地目录
|
||||
*/
|
||||
public DependencyResolver(Path localCacheDir) {
|
||||
this.localCache = localCacheDir;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析并下载指定项目的所有依赖。
|
||||
* <p>
|
||||
* 依赖优先从本地缓存读取,若未命中,则尝试从第一个配置的仓库下载。
|
||||
* </p>
|
||||
*
|
||||
* @param project 要解析依赖的项目
|
||||
* @throws IOException 下载或文件操作失败时抛出
|
||||
*/
|
||||
public void resolve(Project project) throws IOException, URISyntaxException {
|
||||
Files.createDirectories(localCache);
|
||||
for (Dependency dep : project.getDependencies()) {
|
||||
Path jarPath = localCache.resolve(dep.toPath());
|
||||
if (Files.exists(jarPath)) {
|
||||
System.out.println("[dependency] " + dep + " resolved from cache.");
|
||||
continue;
|
||||
}
|
||||
|
||||
// 从第一个仓库下载
|
||||
Optional<Repository> repo = project.getRepositories().values().stream().findFirst();
|
||||
if (repo.isEmpty()) {
|
||||
throw new IOException("No repository configured for dependency " + dep);
|
||||
}
|
||||
|
||||
String url = repo.get().url() + "/" + dep.toPath();
|
||||
download(url, jarPath);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从指定 URL 下载文件到本地目标路径。
|
||||
*
|
||||
* @param urlStr 远程文件 URL
|
||||
* @param dest 本地目标路径
|
||||
* @throws IOException 下载或保存文件时出错
|
||||
*/
|
||||
private void download(String urlStr, Path dest) throws IOException, URISyntaxException {
|
||||
System.out.println("[download] " + urlStr);
|
||||
Files.createDirectories(dest.getParent());
|
||||
URL url = new URI(urlStr).toURL();
|
||||
try (InputStream in = url.openStream()) {
|
||||
Files.copy(in, dest, StandardCopyOption.REPLACE_EXISTING);
|
||||
}
|
||||
System.out.println("[saved] " + dest);
|
||||
}
|
||||
}
|
||||
48
src/main/java/org/jcnc/snow/pkg/tasks/CleanTask.java
Normal file
48
src/main/java/org/jcnc/snow/pkg/tasks/CleanTask.java
Normal file
@ -0,0 +1,48 @@
|
||||
package org.jcnc.snow.pkg.tasks;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Comparator;
|
||||
|
||||
/**
|
||||
* 清理构建输出目录(如 build 和 dist)的任务实现。
|
||||
* <p>
|
||||
* 实现 {@link Task} 接口,通常用于构建流程中的清理阶段。
|
||||
* </p>
|
||||
*/
|
||||
public final class CleanTask implements Task {
|
||||
|
||||
/**
|
||||
* 执行清理任务,删除 "build" 和 "dist" 目录及其所有内容。
|
||||
*
|
||||
* @throws IOException 删除目录过程中出现 IO 错误时抛出
|
||||
*/
|
||||
@Override
|
||||
public void run() throws IOException {
|
||||
deleteDir(Path.of("build"));
|
||||
deleteDir(Path.of("dist"));
|
||||
System.out.println("[clean] done.");
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归删除指定目录及其所有子文件和子目录。
|
||||
* 使用 try-with-resources 自动关闭文件流,避免资源泄漏。
|
||||
*
|
||||
* @param dir 需要删除的目录路径
|
||||
* @throws IOException 删除过程中出现 IO 错误时抛出
|
||||
*/
|
||||
private void deleteDir(Path dir) throws IOException {
|
||||
if (Files.notExists(dir)) return;
|
||||
try (var stream = Files.walk(dir)) {
|
||||
stream.sorted(Comparator.reverseOrder()) // 先删子文件,后删父目录
|
||||
.forEach(p -> {
|
||||
try {
|
||||
Files.delete(p);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
43
src/main/java/org/jcnc/snow/pkg/tasks/CompileTask.java
Normal file
43
src/main/java/org/jcnc/snow/pkg/tasks/CompileTask.java
Normal file
@ -0,0 +1,43 @@
|
||||
package org.jcnc.snow.pkg.tasks;
|
||||
|
||||
import org.jcnc.snow.pkg.model.Project;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
/**
|
||||
* 编译项目源代码的任务实现。
|
||||
* <p>
|
||||
* 实现 {@link Task} 接口,用于构建流程中的编译阶段。当前仅为示例,未集成实际编译器。
|
||||
* </p>
|
||||
*/
|
||||
public final class CompileTask implements Task {
|
||||
|
||||
/** 待编译的项目 */
|
||||
private final Project project;
|
||||
|
||||
/**
|
||||
* 创建 CompileTask 实例。
|
||||
*
|
||||
* @param project 目标项目
|
||||
*/
|
||||
public CompileTask(Project project) {
|
||||
this.project = project;
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行编译任务,打印源代码目录和输出目录。
|
||||
* <p>
|
||||
* 实际编译尚未实现(TODO)。
|
||||
* </p>
|
||||
*
|
||||
* @throws Exception 预留,未来集成编译器可能抛出异常
|
||||
*/
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
// 获取源码目录和输出目录,默认分别为 "src" 和 "build/classes"
|
||||
Path srcDir = Path.of(project.getProperties().getOrDefault("src_dir", "src"));
|
||||
Path outDir = Path.of(project.getProperties().getOrDefault("output_dir", "build/classes"));
|
||||
System.out.println("[compile] sources=" + srcDir + " output=" + outDir);
|
||||
// TODO: 集成实际的编译器
|
||||
}
|
||||
}
|
||||
67
src/main/java/org/jcnc/snow/pkg/tasks/PackageTask.java
Normal file
67
src/main/java/org/jcnc/snow/pkg/tasks/PackageTask.java
Normal file
@ -0,0 +1,67 @@
|
||||
package org.jcnc.snow.pkg.tasks;
|
||||
|
||||
import org.jcnc.snow.pkg.model.Project;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
/**
|
||||
* 项目打包任务,将编译输出目录(如 build/classes)打包为 .ice 文件。
|
||||
* <p>
|
||||
* 实现 {@link Task} 接口,通常用于构建流程中的打包阶段。
|
||||
* </p>
|
||||
*/
|
||||
public final class PackageTask implements Task {
|
||||
|
||||
/** 目标项目 */
|
||||
private final Project project;
|
||||
|
||||
/**
|
||||
* 创建 PackageTask 实例。
|
||||
*
|
||||
* @param project 目标项目
|
||||
*/
|
||||
public PackageTask(Project project) {
|
||||
this.project = project;
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行打包任务,将编译输出目录压缩为 artifact-version.ice 文件。
|
||||
*
|
||||
* @throws Exception 打包过程中出现 IO 或其他异常时抛出
|
||||
*/
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
String artifact = project.getArtifact();
|
||||
String version = project.getVersion();
|
||||
String fileName = artifact + "-" + version + ".ice";
|
||||
Path distDir = Path.of("dist");
|
||||
Files.createDirectories(distDir);
|
||||
Path packageFile = distDir.resolve(fileName);
|
||||
|
||||
try (ZipOutputStream zos = new ZipOutputStream(Files.newOutputStream(packageFile))) {
|
||||
// 仅将编译输出目录打包
|
||||
Path classesDir = Path.of("build/classes");
|
||||
if (Files.exists(classesDir)) {
|
||||
// 使用 try-with-resources 正确关闭 Stream<Path>
|
||||
try (Stream<Path> stream = Files.walk(classesDir)) {
|
||||
stream.filter(Files::isRegularFile)
|
||||
.forEach(p -> {
|
||||
try {
|
||||
// 将文件以相对路径加入压缩包
|
||||
zos.putNextEntry(new ZipEntry(classesDir.relativize(p).toString()));
|
||||
Files.copy(p, zos);
|
||||
zos.closeEntry();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
System.out.println("[package] created " + packageFile);
|
||||
}
|
||||
}
|
||||
35
src/main/java/org/jcnc/snow/pkg/tasks/PublishTask.java
Normal file
35
src/main/java/org/jcnc/snow/pkg/tasks/PublishTask.java
Normal file
@ -0,0 +1,35 @@
|
||||
package org.jcnc.snow.pkg.tasks;
|
||||
|
||||
import org.jcnc.snow.pkg.model.Project;
|
||||
|
||||
/**
|
||||
* 发布项目构件到远程仓库的任务实现。
|
||||
* <p>
|
||||
* 实现 {@link Task} 接口,通常用于构建流程中的发布阶段。目前仅为演示,尚未实现实际上传。
|
||||
* </p>
|
||||
*/
|
||||
public final class PublishTask implements Task {
|
||||
|
||||
/** 目标项目 */
|
||||
private final Project project;
|
||||
|
||||
/**
|
||||
* 创建 PublishTask 实例。
|
||||
*
|
||||
* @param project 目标项目
|
||||
*/
|
||||
public PublishTask(Project project) {
|
||||
this.project = project;
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行发布任务。当前仅打印发布提示,未实现实际上传逻辑。
|
||||
*
|
||||
* @throws Exception 预留,未来实现上传逻辑时可能抛出异常
|
||||
*/
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
// TODO: 实现上传到仓库(如 HTTP PUT/POST)
|
||||
System.out.println("[publish] uploading artifact " + project.getArtifact() + "-" + project.getVersion());
|
||||
}
|
||||
}
|
||||
17
src/main/java/org/jcnc/snow/pkg/tasks/Task.java
Normal file
17
src/main/java/org/jcnc/snow/pkg/tasks/Task.java
Normal file
@ -0,0 +1,17 @@
|
||||
package org.jcnc.snow.pkg.tasks;
|
||||
|
||||
/**
|
||||
* 构建任务的通用接口,所有具体任务都应实现该接口。
|
||||
* <p>
|
||||
* 用于统一生命周期内的任务行为,例如编译、打包、清理等。
|
||||
* </p>
|
||||
*/
|
||||
public interface Task {
|
||||
|
||||
/**
|
||||
* 执行具体任务的入口方法。
|
||||
*
|
||||
* @throws Exception 任务执行过程中出现的异常
|
||||
*/
|
||||
void run() throws Exception;
|
||||
}
|
||||
@ -44,6 +44,6 @@ public class VMLauncher {
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
// Call the method that initializes and runs the VM in DEBUG mode
|
||||
initializeAndRunVM(args, VMMode.DEBUG);
|
||||
initializeAndRunVM(args, VMMode.RUN);
|
||||
}
|
||||
}
|
||||
|
||||
1
src/main/resources/version.properties
Normal file
1
src/main/resources/version.properties
Normal file
@ -0,0 +1 @@
|
||||
snow.version=${project.version}
|
||||
Loading…
x
Reference in New Issue
Block a user