├── docs ├── .vitepress │ ├── .gitignore │ └── config.ts ├── imgs │ ├── build.gif │ ├── e2txt.gif │ ├── info.gif │ ├── txt2e.gif │ ├── fp │ │ ├── build.png │ │ ├── clean.png │ │ └── e2txt.gif │ ├── ebuild_init.png │ ├── envir_var.png │ ├── unzip_e2txt.png │ ├── unzip_ecl.png │ ├── unzip_ebuild.png │ ├── ebuild_toolchain.png │ └── config │ │ ├── run_cmd-args.png │ │ ├── run_e-script.png │ │ └── run_show-envs.png ├── project │ ├── examples.md │ ├── e2txt.md │ ├── environ.md │ ├── build.md │ ├── basic.md │ └── run.md ├── cli │ └── index.md ├── thanks.md ├── index.md ├── README.md ├── first-project.md └── installation.md ├── src ├── EBuild.Test │ ├── test-project │ │ ├── a.e │ │ ├── b.e │ │ ├── scripts │ │ │ └── not-build.e │ │ ├── ebuild.pwd.yaml │ │ └── ebuild.yaml │ ├── Usings.cs │ ├── YamlConverterTests.cs │ ├── ConfigTests.cs │ ├── ToolchainTests.cs │ └── EBuild.Test.csproj └── EBuild │ ├── Hooks │ └── Hook.cs │ ├── Project │ ├── Cleaners │ │ ├── OptionalProjectCleaner.cs │ │ ├── ICleaner.cs │ │ ├── ProjectCleaner.cs │ │ ├── ProjectECodeCleaner.cs │ │ ├── ProjectRecoverECleaner.cs │ │ └── ProjectOutputCleaner.cs │ ├── ProjectPath.cs │ ├── SourcePath.cs │ └── EnvironmentVariables.cs │ ├── Plugins │ ├── Plugin.cs │ ├── IPlugin.cs │ ├── PluginContext.cs │ └── BuildHookScriptPlugin.cs │ ├── Config │ ├── Project.cs │ ├── E2Txt.cs │ ├── Build.cs │ ├── RootConfig.cs │ ├── Target.cs │ └── Resolved │ │ └── ResolvedConfig.cs │ ├── Toolchain │ ├── IToolchain.cs │ ├── ELangToolchain.cs │ ├── E2TxtToolchain.cs │ ├── GeneralToolchain.cs │ └── EclToolchain.cs │ ├── Extensions │ ├── Attributes.cs │ └── ServiceCollection.cs │ ├── Yaml │ └── Converters │ │ ├── EnumAliasAttribute.cs │ │ └── EnumConverter.cs │ ├── DependencyInjection │ ├── TypeResolver.cs │ └── TypeRegistrar.cs │ ├── Consoles │ ├── LinesDisplayer.cs │ ├── MultiTaskDisplayer.cs │ └── TableDisplayer.cs │ ├── EBuild.csproj │ ├── Commands │ ├── SubCommands │ │ ├── Txt2ECommand.cs │ │ ├── ToolchainCommand.cs │ │ ├── CleanCommand.cs │ │ ├── InfoCommand.cs │ │ ├── E2TxtCommand.cs │ │ ├── RunCommand.cs │ │ ├── InitCommand.cs │ │ └── BuildCommand.cs │ └── Base │ │ ├── CommandBase.cs │ │ ├── ProjectCommand.cs │ │ └── TargetCommand.cs │ ├── Global │ └── Defaults.cs │ ├── Sources │ ├── PasswordResolver.cs │ └── ESourceMeta.cs │ └── Program.cs ├── examples ├── .gitignore ├── simple │ ├── ebuild.pwd.yaml │ ├── README.md │ ├── Windows控制台程序.e │ ├── Windows窗口程序.e │ ├── Windows窗口程序--带密码.e │ ├── scripts │ │ └── 易语言做脚本示例.e │ ├── .gitignore │ └── ebuild.yaml └── first-project │ ├── README.md │ ├── Windows控制台程序.e │ ├── .gitignore │ └── ebuild.yaml ├── .editorconfig ├── .idea ├── codeStyles │ └── codeStyleConfig.xml ├── vcs.xml ├── .gitignore ├── .idea.ebuild │ └── .idea │ │ ├── vcs.xml │ │ ├── indexLayout.xml │ │ ├── misc.xml │ │ ├── encodings.xml │ │ ├── .gitignore │ │ └── git_toolbox_prj.xml ├── modules.xml ├── ebuild.iml └── git_toolbox_prj.xml ├── package.json ├── .github └── workflows │ ├── deploy-docs.yaml │ ├── changelog.yml │ └── build.yml ├── .run ├── ebuild.run.xml ├── ebuild info.run.xml ├── ebuild build.run.xml ├── ebuild e2txt.run.xml ├── ebuild init.run.xml ├── ebuild txt2e.run.xml └── ebuild toolchain.run.xml ├── LICENSE ├── ebuild.sln ├── .gitignore └── yarn.lock /docs/.vitepress/.gitignore: -------------------------------------------------------------------------------- 1 | dist/ -------------------------------------------------------------------------------- /src/EBuild.Test/test-project/a.e: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/EBuild.Test/test-project/b.e: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/.gitignore: -------------------------------------------------------------------------------- 1 | bigfiles/ 2 | test-init/ -------------------------------------------------------------------------------- /src/EBuild.Test/test-project/scripts/not-build.e: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | [{*.ts,*.yaml,*.yml}] 2 | indent_size = 2 -------------------------------------------------------------------------------- /examples/simple/ebuild.pwd.yaml: -------------------------------------------------------------------------------- 1 | Windows窗口程序--带密码.e: 123 -------------------------------------------------------------------------------- /src/EBuild.Test/Usings.cs: -------------------------------------------------------------------------------- 1 | global using NUnit.Framework; -------------------------------------------------------------------------------- /src/EBuild.Test/test-project/ebuild.pwd.yaml: -------------------------------------------------------------------------------- 1 | ./b.e: 12345 -------------------------------------------------------------------------------- /docs/imgs/build.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SalHe/ebuild/HEAD/docs/imgs/build.gif -------------------------------------------------------------------------------- /docs/imgs/e2txt.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SalHe/ebuild/HEAD/docs/imgs/e2txt.gif -------------------------------------------------------------------------------- /docs/imgs/info.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SalHe/ebuild/HEAD/docs/imgs/info.gif -------------------------------------------------------------------------------- /docs/imgs/txt2e.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SalHe/ebuild/HEAD/docs/imgs/txt2e.gif -------------------------------------------------------------------------------- /docs/imgs/fp/build.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SalHe/ebuild/HEAD/docs/imgs/fp/build.png -------------------------------------------------------------------------------- /docs/imgs/fp/clean.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SalHe/ebuild/HEAD/docs/imgs/fp/clean.png -------------------------------------------------------------------------------- /docs/imgs/fp/e2txt.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SalHe/ebuild/HEAD/docs/imgs/fp/e2txt.gif -------------------------------------------------------------------------------- /docs/imgs/ebuild_init.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SalHe/ebuild/HEAD/docs/imgs/ebuild_init.png -------------------------------------------------------------------------------- /docs/imgs/envir_var.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SalHe/ebuild/HEAD/docs/imgs/envir_var.png -------------------------------------------------------------------------------- /docs/imgs/unzip_e2txt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SalHe/ebuild/HEAD/docs/imgs/unzip_e2txt.png -------------------------------------------------------------------------------- /docs/imgs/unzip_ecl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SalHe/ebuild/HEAD/docs/imgs/unzip_ecl.png -------------------------------------------------------------------------------- /docs/imgs/unzip_ebuild.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SalHe/ebuild/HEAD/docs/imgs/unzip_ebuild.png -------------------------------------------------------------------------------- /docs/imgs/ebuild_toolchain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SalHe/ebuild/HEAD/docs/imgs/ebuild_toolchain.png -------------------------------------------------------------------------------- /examples/simple/README.md: -------------------------------------------------------------------------------- 1 | # ebuild-example 2 | 3 | 这是由[ebuild](https://github.com/SalHe/ebuild)创建的示例工程。 4 | -------------------------------------------------------------------------------- /examples/simple/Windows控制台程序.e: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SalHe/ebuild/HEAD/examples/simple/Windows控制台程序.e -------------------------------------------------------------------------------- /examples/simple/Windows窗口程序.e: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SalHe/ebuild/HEAD/examples/simple/Windows窗口程序.e -------------------------------------------------------------------------------- /docs/imgs/config/run_cmd-args.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SalHe/ebuild/HEAD/docs/imgs/config/run_cmd-args.png -------------------------------------------------------------------------------- /docs/imgs/config/run_e-script.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SalHe/ebuild/HEAD/docs/imgs/config/run_e-script.png -------------------------------------------------------------------------------- /docs/imgs/config/run_show-envs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SalHe/ebuild/HEAD/docs/imgs/config/run_show-envs.png -------------------------------------------------------------------------------- /examples/first-project/README.md: -------------------------------------------------------------------------------- 1 | # ebuild-example 2 | 3 | 这是由[ebuild](https://github.com/SalHe/ebuild)创建的示例工程。 4 | -------------------------------------------------------------------------------- /examples/simple/Windows窗口程序--带密码.e: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SalHe/ebuild/HEAD/examples/simple/Windows窗口程序--带密码.e -------------------------------------------------------------------------------- /examples/simple/scripts/易语言做脚本示例.e: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SalHe/ebuild/HEAD/examples/simple/scripts/易语言做脚本示例.e -------------------------------------------------------------------------------- /examples/first-project/Windows控制台程序.e: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SalHe/ebuild/HEAD/examples/first-project/Windows控制台程序.e -------------------------------------------------------------------------------- /src/EBuild/Hooks/Hook.cs: -------------------------------------------------------------------------------- 1 | namespace EBuild.Hooks; 2 | 3 | public enum Hook 4 | { 5 | PreBuild, 6 | PostBuild 7 | } -------------------------------------------------------------------------------- /examples/simple/.gitignore: -------------------------------------------------------------------------------- 1 | # 恢复出来的易语言源文件和密码文件不纳入版本控制 2 | *.recover.e 3 | #ebuild.pwd.yaml 4 | **/*.ecode/log 5 | 6 | # 易语言产生的备份源码文件 7 | *.bak 8 | 9 | # 编译输出 10 | ebuild-out/ -------------------------------------------------------------------------------- /docs/project/examples.md: -------------------------------------------------------------------------------- 1 | # 案例 2 | 3 | 关于更加完整的配置,您可以参考: 4 | 5 | - [ebuild 官方案例](https://github.com/SalHe/ebuild/tree/master/examples) 6 | 7 | 或者如果您想在这里展示您的工程配置,欢迎在GitHub上编辑本文件以在此展示您的工程。 -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /examples/first-project/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # 恢复出来的易语言源文件和密码文件不纳入版本控制 3 | *.recover.e 4 | ebuild.pwd.yaml 5 | **/*.ecode/log 6 | 7 | # 易语言产生的备份源码文件 8 | *.bak 9 | 10 | 11 | # 编译输出 12 | ebuild-out/ -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/EBuild/Project/Cleaners/OptionalProjectCleaner.cs: -------------------------------------------------------------------------------- 1 | namespace EBuild.Project.Cleaners; 2 | 3 | public abstract class OptionalProjectCleaner : ProjectCleaner 4 | { 5 | public override bool Optional => true; 6 | } -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Editor-based HTTP Client requests 5 | /httpRequests/ 6 | # Datasource local storage ignored files 7 | /dataSources/ 8 | /dataSources.local.xml 9 | -------------------------------------------------------------------------------- /.idea/.idea.ebuild/.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/.idea.ebuild/.idea/indexLayout.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /docs/project/e2txt.md: -------------------------------------------------------------------------------- 1 | # e2txt相关配置 2 | 3 | ```yaml 4 | e2txt: 5 | name-style: 中文 # 命名风格:中文、英文 6 | generate-e: true # 生成易语言代码文件 7 | ``` 8 | 9 | 关于此处配置的具体含义请参考`e2txt`官方说明。 10 | 11 | ::: tip 12 | 13 | 这些配置将影响生成的代码文件结构等,建议在工程建立之初确定,后期不做改动。 14 | 15 | ::: -------------------------------------------------------------------------------- /.idea/.idea.ebuild/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /src/EBuild/Project/Cleaners/ICleaner.cs: -------------------------------------------------------------------------------- 1 | namespace EBuild.Project.Cleaners; 2 | 3 | public interface ICleaner 4 | { 5 | bool Optional { get; } 6 | bool Once { get; } 7 | string CleanContent { get; } 8 | void Clean(TTarget target, TProject projectConfig); 9 | } -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/EBuild/Plugins/Plugin.cs: -------------------------------------------------------------------------------- 1 | using EBuild.Hooks; 2 | 3 | namespace EBuild.Plugins; 4 | 5 | public class Plugin : IPlugin 6 | { 7 | public virtual Task OnHook(PluginContext context, CancellationToken cancellationToken, Hook hook) 8 | { 9 | return Task.FromResult(true); 10 | } 11 | } -------------------------------------------------------------------------------- /.idea/.idea.ebuild/.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/ebuild.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/EBuild/Config/Project.cs: -------------------------------------------------------------------------------- 1 | namespace EBuild.Config; 2 | 3 | public class Project 4 | { 5 | public string Name { get; set; } 6 | public string Version { get; set; } 7 | public string Description { get; set; } 8 | public string Author { get; set; } 9 | public string Repository { get; set; } 10 | public string Homepage { get; set; } 11 | } -------------------------------------------------------------------------------- /.idea/.idea.ebuild/.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Rider ignored files 5 | /contentModel.xml 6 | /projectSettingsUpdater.xml 7 | /modules.xml 8 | /.idea.ebuild.iml 9 | # Editor-based HTTP Client requests 10 | /httpRequests/ 11 | # Datasource local storage ignored files 12 | /dataSources/ 13 | /dataSources.local.xml 14 | -------------------------------------------------------------------------------- /src/EBuild/Config/E2Txt.cs: -------------------------------------------------------------------------------- 1 | using EBuild.Yaml.Converters; 2 | 3 | namespace EBuild.Config; 4 | 5 | public class E2Txt 6 | { 7 | public enum NameStyleEnum 8 | { 9 | [EnumAlias("英文")] English, 10 | [EnumAlias("中文")] Chinese 11 | } 12 | 13 | public NameStyleEnum NameStyle { get; set; } 14 | public bool GenerateE { get; set; } 15 | } -------------------------------------------------------------------------------- /docs/cli/index.md: -------------------------------------------------------------------------------- 1 | # 命令行帮助文档 2 | 3 | 由于目前`ebuild`使用[C#](https://docs.microsoft.com/zh-cn/dotnet/csharp/)基于[.NET6](https://dotnet.microsoft.com/zh-cn/)重写,所使用的命令行解析库暂时不支持自动生成文档,所以命令行帮助文档目前暂时无法及时跟进。 4 | 5 | 此外,非常欢迎您为`ebuild`编写文档。 6 | 7 | ::: tip 为何要重写? 8 | 关于为何重写,您可以参见[使用C#+.NET6重写ebuild,为后续开发打基础](https://github.com/SalHe/ebuild/pull/1)。 9 | ::: 10 | 11 | 如果您需要查看`ebuild`的命令行帮助文档,建议在命令行中为`ebuild`键入`--help`参数查看详细的帮助信息。 -------------------------------------------------------------------------------- /docs/thanks.md: -------------------------------------------------------------------------------- 1 | # 特别鸣谢 2 | 3 | `ebuild`的存在离不开以下作者和项目为易语言生态带来的贡献: 4 | 5 | - [e2txt](http://e2ee.jimstone.com.cn/) by [JimStone](http://e2ee.jimstone.com.cn/) 6 | - [易语言命令行编译工具 ecl](https://bbs.125.la/forum.php?mod=viewthread&tid=14553929&highlight=ecl) 7 | by [被封七号](https://bbs.125.la/home.php?mod=space&uid=504218&do=thread&type=thread&view=me&from=space) 8 | 9 | > 如有任何形式的对于作者或项目的侵犯行为,请见谅,并联系我对项目做出调整或者下架。 10 | -------------------------------------------------------------------------------- /src/EBuild/Project/Cleaners/ProjectCleaner.cs: -------------------------------------------------------------------------------- 1 | using EBuild.Config.Resolved; 2 | 3 | namespace EBuild.Project.Cleaners; 4 | 5 | public abstract class ProjectCleaner : ICleaner 6 | { 7 | public virtual bool Optional => false; 8 | public virtual bool Once => false; 9 | public abstract string CleanContent { get; } 10 | public abstract void Clean(ResolvedTarget target, ResolvedConfig projectConfig); 11 | } -------------------------------------------------------------------------------- /src/EBuild/Toolchain/IToolchain.cs: -------------------------------------------------------------------------------- 1 | namespace EBuild.Toolchain; 2 | 3 | public record EnvironmentVariable(string VariableName, string Description, Func Value); 4 | 5 | public interface IToolchain 6 | { 7 | public string Description { get; } 8 | public string Link { get; } 9 | public string ExecutablePath { get; } 10 | public IList EnvironmentVariables { get; } 11 | public void Search(string projectRootDir); 12 | public bool Exists(); 13 | } -------------------------------------------------------------------------------- /src/EBuild/Config/Build.cs: -------------------------------------------------------------------------------- 1 | using EBuild.Yaml.Converters; 2 | 3 | namespace EBuild.Config; 4 | 5 | public enum Compiler 6 | { 7 | [EnumAlias("静态编译")] Static, 8 | [EnumAlias("普通编译")] Normal, 9 | [EnumAlias("独立编译")] Independent, 10 | [EnumAlias("黑月编译")] BlackMoon, 11 | [EnumAlias("黑月汇编")] BlackMoonAsm, 12 | [EnumAlias("黑月C++")] BlackMoonCpp, 13 | [EnumAlias("黑月MFC")] BlackMoonMFC 14 | } 15 | 16 | public class Build 17 | { 18 | public Compiler Compiler { get; set; } 19 | } -------------------------------------------------------------------------------- /src/EBuild/Project/Cleaners/ProjectECodeCleaner.cs: -------------------------------------------------------------------------------- 1 | using EBuild.Config.Resolved; 2 | 3 | namespace EBuild.Project.Cleaners; 4 | 5 | public class ProjectECodeCleaner : OptionalProjectCleaner 6 | { 7 | public override string CleanContent => "*.ecode(文本格式代码文件夹)"; 8 | 9 | public override void Clean(ResolvedTarget target, ResolvedConfig projectConfig) 10 | { 11 | Console.WriteLine($"正在删除 {target.Target.GetECodeDir()}"); 12 | Directory.Delete(target.Target.GetECodeDir(), true); 13 | } 14 | } -------------------------------------------------------------------------------- /src/EBuild/Project/Cleaners/ProjectRecoverECleaner.cs: -------------------------------------------------------------------------------- 1 | using EBuild.Config.Resolved; 2 | 3 | namespace EBuild.Project.Cleaners; 4 | 5 | public class ProjectRecoverECleaner : ProjectCleaner 6 | { 7 | public override string CleanContent => "*.recover.e(由文本格式代码恢复的源文件)"; 8 | 9 | public override void Clean(ResolvedTarget target, ResolvedConfig projectConfig) 10 | { 11 | Console.WriteLine($"正在删除 {target.Target.GetRecoverEPath()}"); 12 | File.Delete(target.Target.GetRecoverEPath()); 13 | } 14 | } -------------------------------------------------------------------------------- /src/EBuild/Plugins/IPlugin.cs: -------------------------------------------------------------------------------- 1 | using EBuild.Config.Resolved; 2 | using EBuild.Hooks; 3 | 4 | namespace EBuild.Plugins; 5 | 6 | public interface IPlugin 7 | { 8 | /// 9 | /// 当触发指定构建时期时,会调用该函数。 10 | /// 11 | /// 12 | /// 13 | /// 14 | /// 15 | Task OnHook(PluginContext context, CancellationToken cancellationToken, Hook hook); 16 | } -------------------------------------------------------------------------------- /examples/first-project/ebuild.yaml: -------------------------------------------------------------------------------- 1 | project: 2 | name: ebuild-example 3 | version: "1.0" 4 | description: 这是由ebuild创建的示例工程。 5 | author: SalHe 6 | repository: https://github.com/SalHe/ebuild 7 | homepage: https://github.com/SalHe 8 | excludes: 9 | - '**/*.recover.e' 10 | - '**/*.ecode/**.e' 11 | - '**/*.代码/**.e' 12 | exclude-builds: 13 | - './scripts/**/*.e' # 脚本文件不纳入'ebuild build'命令中进行自动构建 14 | includes: 15 | - '**/*.e' 16 | e2txt: 17 | name-style: 中文 18 | generate-e: true 19 | build: 20 | compiler: 独立编译 21 | -------------------------------------------------------------------------------- /.idea/git_toolbox_prj.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 14 | 15 | -------------------------------------------------------------------------------- /src/EBuild/Project/Cleaners/ProjectOutputCleaner.cs: -------------------------------------------------------------------------------- 1 | using EBuild.Config.Resolved; 2 | 3 | namespace EBuild.Project.Cleaners; 4 | 5 | public class ProjectOutputCleaner : ProjectCleaner 6 | { 7 | public override string CleanContent => "编译结果"; 8 | public override bool Once => true; // 与最原始的 golang 实现版的行为保持一致 9 | 10 | public override void Clean(ResolvedTarget target, ResolvedConfig projectConfig) 11 | { 12 | Console.WriteLine($"正在删除 {projectConfig.OutputDir}", true); 13 | Directory.Delete(projectConfig.OutputDir, true); 14 | } 15 | } -------------------------------------------------------------------------------- /src/EBuild/Extensions/Attributes.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | 3 | namespace EBuild.Extensions; 4 | 5 | public static class Attributes 6 | { 7 | public static bool GetEnumValueAttribute(TEnum enumValue, out TAttribute? attribute) 8 | where TEnum : Enum 9 | where TAttribute : Attribute 10 | { 11 | var type = enumValue.GetType(); 12 | var enumName = type.GetEnumName(enumValue)!; 13 | attribute = type.GetField(enumName)?.GetCustomAttribute(); 14 | return attribute != null; 15 | } 16 | } -------------------------------------------------------------------------------- /.idea/.idea.ebuild/.idea/git_toolbox_prj.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 14 | 15 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ebuild", 3 | "version": "1.1.0.0", 4 | "main": "index.js", 5 | "repository": "https://github.com/SalHe/ebuild.git", 6 | "author": "SalHe Li ", 7 | "license": "MIT", 8 | "private": true, 9 | "scripts": { 10 | "docs:dev": "vitepress dev docs", 11 | "docs:build": "vitepress build docs", 12 | "docs:serve": "vitepress serve docs" 13 | }, 14 | "devDependencies": { 15 | "@types/markdown-it": "^12.2.3", 16 | "markdown-it-task-checkbox": "^1.0.6", 17 | "vitepress": "^1.0.0-alpha.4", 18 | "vue": "^3.2.37" 19 | } 20 | } -------------------------------------------------------------------------------- /src/EBuild/Project/ProjectPath.cs: -------------------------------------------------------------------------------- 1 | namespace EBuild.Project; 2 | 3 | public static class ProjectPath 4 | { 5 | public static string GetConfigFilePath(string projectRootDir) 6 | { 7 | return Path.GetFullPath("ebuild.yaml", projectRootDir); 8 | } 9 | 10 | public static string GetSourcePasswordFilePath(string projectRootDir) 11 | { 12 | return Path.GetFullPath("ebuild.pwd.yaml", projectRootDir); 13 | } 14 | 15 | public static string GetDefaultOutputPath(string projectRootDir) 16 | { 17 | return Path.GetFullPath("ebuild-out", projectRootDir); 18 | } 19 | } -------------------------------------------------------------------------------- /.github/workflows/deploy-docs.yaml: -------------------------------------------------------------------------------- 1 | name: Deploy Docs 2 | 3 | on: 4 | push: 5 | paths: 'docs/**' 6 | branches: 7 | - master 8 | 9 | jobs: 10 | deploy: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v2 14 | - uses: actions/setup-node@v3 15 | with: 16 | node-version: 16 17 | cache: yarn 18 | - run: yarn install --frozen-lockfile 19 | 20 | - name: Build 21 | run: yarn docs:build 22 | 23 | - name: Deploy 24 | uses: peaceiris/actions-gh-pages@v3 25 | with: 26 | github_token: ${{ secrets.GITHUB_TOKEN }} 27 | publish_dir: docs/.vitepress/dist 28 | -------------------------------------------------------------------------------- /src/EBuild/Extensions/ServiceCollection.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics.CodeAnalysis; 2 | using Microsoft.Extensions.DependencyInjection; 3 | 4 | namespace EBuild.Extensions; 5 | 6 | internal static class ServiceCollection 7 | { 8 | public static void AddImplementation( 10 | this IServiceCollection services) 11 | where TService : class 12 | where TImplementation : class, TService 13 | { 14 | services.AddSingleton(); 15 | services.AddSingleton(s => s.GetService()); 16 | } 17 | } -------------------------------------------------------------------------------- /src/EBuild/Project/SourcePath.cs: -------------------------------------------------------------------------------- 1 | using EBuild.Config; 2 | 3 | namespace EBuild.Project; 4 | 5 | public static class SourcePath 6 | { 7 | public static string GetECodeDir(string source) 8 | { 9 | return Path.ChangeExtension(source, "ecode"); 10 | } 11 | 12 | public static string GetRecoverEPath(string source) 13 | { 14 | return Path.ChangeExtension(source, "recover.e"); 15 | } 16 | 17 | public static string GetECodeDir(this Target target) 18 | { 19 | return GetECodeDir(target.Source); 20 | } 21 | 22 | public static string GetRecoverEPath(this Target target) 23 | { 24 | return GetRecoverEPath(target.Source); 25 | } 26 | } -------------------------------------------------------------------------------- /src/EBuild/Config/RootConfig.cs: -------------------------------------------------------------------------------- 1 | using YamlDotNet.Serialization; 2 | 3 | namespace EBuild.Config; 4 | 5 | public class RootConfig 6 | { 7 | public Project Project { get; set; } 8 | public IDictionary? Scripts { get; set; } 9 | public IList Excludes { get; set; } = new List(); 10 | public IList Includes { get; set; } = new List(); 11 | public IList ExcludeBuilds { get; set; } = new List(); 12 | 13 | [YamlMember(Alias = "e2txt")] public E2Txt E2Txt { get; set; } 14 | 15 | public Build Build { get; set; } = new Build() 16 | { 17 | Compiler = Compiler.Normal 18 | }; 19 | 20 | public IList? Targets { get; set; } 21 | } -------------------------------------------------------------------------------- /src/EBuild/Yaml/Converters/EnumAliasAttribute.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | 3 | namespace EBuild.Yaml.Converters; 4 | 5 | [AttributeUsage(AttributeTargets.Field)] 6 | public class EnumAliasAttribute : Attribute 7 | { 8 | public EnumAliasAttribute(string name) 9 | { 10 | Name = name; 11 | } 12 | 13 | public string Name { get; set; } 14 | 15 | public static EnumAliasAttribute? GetEnumAliasAttribute(object? enumValue) 16 | { 17 | if (enumValue == null) return null; 18 | var type = enumValue.GetType(); 19 | if (type is not { IsEnum: true }) return null; 20 | var enumName = type.GetEnumName(enumValue)!; 21 | return type.GetField(enumName)?.GetCustomAttribute(); 22 | } 23 | } -------------------------------------------------------------------------------- /src/EBuild.Test/YamlConverterTests.cs: -------------------------------------------------------------------------------- 1 | using EBuild.Config; 2 | using EBuild.Yaml.Converters; 3 | using YamlDotNet.Core; 4 | using YamlDotNet.Serialization; 5 | 6 | namespace EBuild.Test; 7 | 8 | public class YamlConverterTests 9 | { 10 | [SetUp] 11 | public void Setup() 12 | { 13 | } 14 | 15 | [Test] 16 | public void TestEnumConverter() 17 | { 18 | var doc = @" 19 | - BlackMoon 20 | - black-moon 21 | - 黑月编译 22 | "; 23 | var compilers = new DeserializerBuilder() 24 | .WithTypeConverter(EnumConverter.Instance) 25 | .Build() 26 | .Deserialize(new Parser(new StringReader(doc))); 27 | foreach (var compiler in compilers) Assert.That(compiler, Is.EqualTo(Compiler.BlackMoon)); 28 | } 29 | } -------------------------------------------------------------------------------- /src/EBuild/DependencyInjection/TypeResolver.cs: -------------------------------------------------------------------------------- 1 | using Spectre.Console.Cli; 2 | 3 | namespace EBuild.DependencyInjection; 4 | 5 | public sealed class TypeResolver : ITypeResolver, IDisposable 6 | { 7 | private readonly IServiceProvider _provider; 8 | 9 | public TypeResolver(IServiceProvider provider) 10 | { 11 | _provider = provider ?? throw new ArgumentNullException(nameof(provider)); 12 | } 13 | 14 | public object Resolve(Type type) 15 | { 16 | if (type == null) 17 | { 18 | return null; 19 | } 20 | 21 | return _provider.GetService(type); 22 | } 23 | 24 | public void Dispose() 25 | { 26 | if (_provider is IDisposable disposable) 27 | { 28 | disposable.Dispose(); 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: home 3 | 4 | title: E-Build 5 | titleTemplate: E-Build 是一个专注于易语言构建的工具。 6 | 7 | hero: 8 | name: E-Build 9 | text: E-Build 是一个专注于易语言自动化构建的工具。 10 | tagline: 基于 e2txt 和 ecl 的专注于易语言自动化构建的工具。 11 | actions: 12 | - theme: brand 13 | text: 开始了解 14 | link: ./README 15 | - theme: alt 16 | text: 查看源码 17 | link: https://github.com/SalHe/ebuild 18 | 19 | features: 20 | - title: 版本控制 21 | details: 可快速将工程中的易语言源代码借助 e2txt 转换成文本格式的代码(或者反向转换),利于版本控制和多人协同开发. 22 | - title: 自动构建 23 | details: 使用配置化的方式将易语言源代码纳入工程,且可以自定义构建目标,使用不同的构建细节借助 ecl 去完成源码编译,不需要反复开启易语言编译. 24 | - title: 脚本执行 25 | details: 可以将工程相关的命令、脚本纳入到工程配置中方便执行,同时还可以使用易语言源文件编写脚本,由 ebuild 自动帮助您编译并执行. 26 | - title: 生命周期 27 | details: 为工程的构建定义了一些生命周期,您可以在这些生命周期执行一些您想要的工作,比如将文件安装到某处等,方便了您的构建过程. 28 | --- -------------------------------------------------------------------------------- /src/EBuild/Plugins/PluginContext.cs: -------------------------------------------------------------------------------- 1 | using EBuild.Commands.Base; 2 | using EBuild.Config.Resolved; 3 | using EBuild.Project; 4 | 5 | namespace EBuild.Plugins; 6 | 7 | public class PluginContext 8 | { 9 | public delegate void UpdateStatusDelegate(TargetStatus status, string log); 10 | 11 | public ResolvedTarget? BuildTarget { get; internal init; } = null; 12 | public ResolvedConfig ProjectConfig { get; } 13 | public EnvironmentVariables EnvironmentVariables { get; } 14 | public CancellationToken CancellationToken { get; internal init; } 15 | public UpdateStatusDelegate? UpdateStatus { get; internal init; } 16 | 17 | public PluginContext(ResolvedConfig projectConfig, EnvironmentVariables environmentVariables) 18 | { 19 | ProjectConfig = projectConfig; 20 | EnvironmentVariables = environmentVariables; 21 | } 22 | } -------------------------------------------------------------------------------- /src/EBuild/Consoles/LinesDisplayer.cs: -------------------------------------------------------------------------------- 1 | using Spectre.Console; 2 | 3 | namespace EBuild.Consoles; 4 | 5 | public class LinesDisplayer : MultiTaskDisplayer 6 | { 7 | public override void SetHeader(string header) 8 | { 9 | AnsiConsole.MarkupLine(header); 10 | } 11 | 12 | public override void Update(TTarget target, TStatus status, string log) 13 | { 14 | var content = $"{Markup.Escape("[") + Markup.Escape(TargetName(target)) + Markup.Escape("]")} > {log}"; 15 | AnsiConsole.MarkupLine(string.Format(StatusString(status), content)); 16 | } 17 | 18 | public override Task StartAsync(Func, Task> action) 19 | { 20 | AnsiConsole.Profile.Width = int.MaxValue; // 防止长日志自动折行(soft-warp) 21 | return action(this); 22 | } 23 | } -------------------------------------------------------------------------------- /src/EBuild.Test/test-project/ebuild.yaml: -------------------------------------------------------------------------------- 1 | project: 2 | name: ebuild-example 3 | version: "1.0" 4 | description: 这是由ebuild创建的示例工程。 5 | author: SalHe 6 | repository: https://github.com/SalHe/ebuild 7 | homepage: https://github.com/SalHe 8 | excludes: 9 | - '**/*.recover.e' 10 | - '**/*.ecode/**.e' 11 | - '**/*.代码/**.e' 12 | exclude-builds: 13 | - './scripts/**/*.e' # 脚本文件不纳入'ebuild build'命令中进行自动构建 14 | includes: 15 | - '**/*.e' 16 | e2txt: 17 | name-style: 中文 18 | generate-e: true 19 | build: 20 | compiler: 独立编译 21 | targets: 22 | - name: a.e——静态编译版 23 | description: 这是一个简单的控制台程序,会在标准输出输出一句问候。 24 | source: ./a.e 25 | output: a——静态编译版.exe 26 | build: 27 | compiler: 静态编译 28 | 29 | - name: a——黑月版 30 | description: 这是一个简单的控制台程序,会在标准输出输出一句问候。 31 | source: ./a.e 32 | output: a——黑月版.exe 33 | build: 34 | compiler: 黑月编译 35 | -------------------------------------------------------------------------------- /src/EBuild/Config/Target.cs: -------------------------------------------------------------------------------- 1 | using EBuild.Hooks; 2 | 3 | namespace EBuild.Config; 4 | 5 | public class Target 6 | { 7 | public string Name { get; set; } 8 | public string Description { get; set; } 9 | public string Source { get; set; } 10 | public string Output { get; set; } 11 | public bool Package { get; set; } 12 | public IDictionary Hooks { get; set; } 13 | public Build? Build { get; set; } 14 | public string CompileConfig { get; set; } 15 | public string CompileDescription { get; set; } 16 | 17 | public string DisplayName(string projectRootDir) 18 | { 19 | return string.IsNullOrEmpty(Name) 20 | ? Path.GetRelativePath(projectRootDir, Source) 21 | : Name; 22 | } 23 | 24 | public string OutputPath(string outputDir) 25 | { 26 | return Path.GetFullPath(Output, outputDir); 27 | } 28 | } -------------------------------------------------------------------------------- /src/EBuild/EBuild.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net6.0 6 | enable 7 | enable 8 | 1.1.0 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/EBuild/Toolchain/ELangToolchain.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Win32; 2 | 3 | namespace EBuild.Toolchain; 4 | 5 | public class ELangToolchain : GeneralToolchain 6 | { 7 | public override IList EnvironmentVariables => new List() 8 | { 9 | new("ELANG_DIR", "易语言安装路径", () => Path.GetDirectoryName(ExecutablePath) ?? ""), 10 | }; 11 | 12 | public ELangToolchain() : base("易语言", "http://www.eyuyan.com/pdown.htm", "e") 13 | { 14 | } 15 | 16 | public override void Search(string projectRootDir) 17 | { 18 | base.Search(projectRootDir); 19 | if (!Exists()) 20 | { 21 | using var registryKey = Registry.CurrentUser.OpenSubKey("Software\\FlySky\\E\\Install"); 22 | var eLibDir = registryKey.GetValue("Path")?.ToString() ?? ""; 23 | ExecutablePath = Path.Combine(Path.GetFullPath("..", eLibDir), "e.exe"); 24 | if (!File.Exists(ExecutablePath)) ExecutablePath = ""; 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /.run/ebuild.run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 20 | -------------------------------------------------------------------------------- /.run/ebuild info.run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 20 | -------------------------------------------------------------------------------- /.run/ebuild build.run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 20 | -------------------------------------------------------------------------------- /.run/ebuild e2txt.run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 20 | -------------------------------------------------------------------------------- /.run/ebuild init.run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 20 | -------------------------------------------------------------------------------- /.run/ebuild txt2e.run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 SalHe Li 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/EBuild/DependencyInjection/TypeRegistrar.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | using Spectre.Console.Cli; 3 | 4 | namespace EBuild.DependencyInjection; 5 | 6 | public sealed class TypeRegistrar : ITypeRegistrar 7 | { 8 | private readonly IServiceCollection _builder; 9 | 10 | public TypeRegistrar(IServiceCollection builder) 11 | { 12 | _builder = builder; 13 | } 14 | 15 | public ITypeResolver Build() 16 | { 17 | return new TypeResolver(_builder.BuildServiceProvider()); 18 | } 19 | 20 | public void Register(Type service, Type implementation) 21 | { 22 | _builder.AddSingleton(service, implementation); 23 | } 24 | 25 | public void RegisterInstance(Type service, object implementation) 26 | { 27 | _builder.AddSingleton(service, implementation); 28 | } 29 | 30 | public void RegisterLazy(Type service, Func func) 31 | { 32 | if (func is null) 33 | { 34 | throw new ArgumentNullException(nameof(func)); 35 | } 36 | 37 | _builder.AddSingleton(service, (provider) => func()); 38 | } 39 | } -------------------------------------------------------------------------------- /.run/ebuild toolchain.run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 20 | -------------------------------------------------------------------------------- /src/EBuild/Consoles/MultiTaskDisplayer.cs: -------------------------------------------------------------------------------- 1 | namespace EBuild.Consoles; 2 | 3 | public interface IMultiTaskDisplayer 4 | { 5 | public Func TargetName { get; set; } 6 | public void SetHeader(string header); 7 | public void Update(TTarget target, TTargetStatus status, string log); 8 | public Task StartAsync(Func, Task> action); 9 | 10 | public virtual Action GetUpdater(TTarget target) 11 | { 12 | return (status, log) => Update(target, status, log); 13 | } 14 | } 15 | 16 | public abstract class MultiTaskDisplayer : IMultiTaskDisplayer 17 | { 18 | private IMultiTaskDisplayer _multiTaskDisplayerImplementation; 19 | public Func TargetName { get; set; } 20 | public Func StatusString { get; set; } 21 | public abstract void SetHeader(string header); 22 | public abstract void Update(TTarget target, TStatus status, string log); 23 | public abstract Task StartAsync(Func, Task> action); 24 | } -------------------------------------------------------------------------------- /docs/project/environ.md: -------------------------------------------------------------------------------- 1 | # 环境变量 2 | 3 | 在[执行脚本](./run.md#工程相关脚本)、[执行易语言源文件](./run.md#使用易语言程序作为脚本)以及[执行构建目标生命周期脚本](./build.md#构建生命周期相关脚本)的时候,`ebuild`会设置一些环境变量用于辅助脚本的编写,以下对所设置的环境变量做出说明。 4 | 5 | | 环境变量名 | 说明 | 执行脚本/易语言源文件 | 构建生命周期 | 6 | | ------------------------- | ---------------------- | --------------------- | ------------ | 7 | | EBUILD_EXECUTABLE_PATH | `ebuild`可执行文件路径 | ✔ | ✔ | 8 | | ELANG_DIR | `易语言`安装目录 | ✔ | ✔ | 9 | | ECL_DIR | `ecl`安装目录 | ✔ | ✔ | 10 | | E2TXT_DIR | `e2txt`安装目录 | ✔ | ✔ | 11 | | EBUILD_PROJECT_ROOT_DIR | 工程根目录 | ✔ | ✔ | 12 | | EBUILD_PROJECT_OUTPUT_DIR | 构建输出目录 | ✔ | ✔ | 13 | | EBUILD_PERIOD | 构建生命周期 | ❌ | ✔ | 14 | | EBUILD_SOURCE_FILE | 被构建的源文件 | ❌ | ✔ | 15 | | EBUILD_TARGET_FILE | 构建目标输出路径 | ❌ | ✔ | 16 | | EBUILD_TARGET_TYPE | 构建目标类型 | ❌ | ✔ | -------------------------------------------------------------------------------- /src/EBuild/Commands/SubCommands/Txt2ECommand.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | using EBuild.Config.Resolved; 3 | using EBuild.Plugins; 4 | using EBuild.Project; 5 | using EBuild.Toolchain; 6 | using YamlDotNet.Serialization; 7 | 8 | namespace EBuild.Commands.SubCommands; 9 | 10 | [Description("将文本格式的代码转换为易语言源文件。")] 11 | public class Txt2ECommand : E2TxtCommand 12 | { 13 | public Txt2ECommand(IDeserializer deserializer, E2TxtToolchain e2txt, IEnumerable plugins) : base( 14 | deserializer, e2txt, plugins) 15 | { 16 | } 17 | 18 | protected override string GenerateHeading(WholeStatus status) 19 | { 20 | switch (status) 21 | { 22 | case WholeStatus.Doing: 23 | return "txt2e"; 24 | case WholeStatus.Completed: 25 | return "[green]:check_mark:txt2e[/]"; 26 | case WholeStatus.ErrorOccured: 27 | return "[red]:cross_mark:txt2e[/]"; 28 | } 29 | 30 | return ""; 31 | } 32 | 33 | protected override IList GetArgs(ResolvedTarget target) 34 | { 35 | return E2TxtToolchain.Txt2EArgs( 36 | target.Target.GetECodeDir(), target.Target.GetRecoverEPath(), _resolvedConfig.RootConfig.E2Txt); 37 | } 38 | } -------------------------------------------------------------------------------- /src/EBuild/Project/EnvironmentVariables.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Specialized; 2 | using EBuild.Toolchain; 3 | 4 | namespace EBuild.Project; 5 | 6 | public class EnvironmentVariables : List, ICloneable 7 | { 8 | private readonly IEnumerable _toolchains; 9 | 10 | public EnvironmentVariables(IEnumerable toolchains) 11 | { 12 | _toolchains = toolchains; 13 | } 14 | 15 | public void ForProject(string projectRoot, string outputDir) 16 | { 17 | foreach (var toolchain in _toolchains) 18 | { 19 | toolchain.Search(projectRoot); 20 | AddRange(toolchain.EnvironmentVariables); 21 | } 22 | 23 | Add(new("EBUILD_PROJECT_ROOT_DIR", "工程根目录", () => projectRoot)); 24 | Add(new("EBUILD_PROJECT_OUTPUT_DIR", "构建输出目录", () => outputDir)); 25 | } 26 | 27 | public void LoadToStringDictionary(StringDictionary d) 28 | { 29 | foreach (var variable in this) 30 | { 31 | d[variable.VariableName] = variable.Value(); 32 | } 33 | } 34 | 35 | public object Clone() 36 | { 37 | var evs = new EnvironmentVariables(_toolchains); 38 | this.ForEach(evs.Add); 39 | return evs; 40 | } 41 | } -------------------------------------------------------------------------------- /docs/project/build.md: -------------------------------------------------------------------------------- 1 | # 构建(编译) 2 | 3 | 您可以全局性的指定编译源文件时采用的编译方式,如:独立编译、静态编译等。也可以单独为目标(`target`)指定构建配置。 4 | 5 | 对于未指定构建配置的目标将采用全局的构建配置。 6 | 7 | 8 | ## 全局配置 9 | 10 | ```yaml 11 | project: 12 | # ...... 13 | build: 14 | compiler: 独立编译 15 | ``` 16 | 17 | 在上述配置中,指定了全局默认的构建配置采用的编译方式为“独立编译”,您可以指定其他的[编译方式](#编译方式)。 18 | 19 | ## 编译方式 20 | 21 | 支持的编译方式如下: 22 | 23 | - 黑月编译 24 | - 黑月汇编 25 | - 黑月C++ 26 | - 黑月MFC 27 | - 静态编译 28 | - 独立编译 29 | 30 | ::: info 31 | 编译方式定义于: https://github.com/SalHe/ebuild/blob/master/config/source.go 32 | ::: 33 | 34 | ## 特定目标配置 35 | 36 | 您可以在`targets`下配置若干构建目标,对目标的输出文件名、编译方式等做出说明。 37 | 38 | 配置格式如下: 39 | 40 | ```yaml 41 | projects: 42 | # ...... 43 | targets: 44 | - name: <目标的名称> 45 | description: <目标的描述> 46 | source: <目标的源文件路径> 47 | build: 48 | compiler: <编译方式> 49 | output: <输出文件名> # 当使用相对路径时,将相对于构建输出目录 50 | package: false # 是否为易包 51 | hooks: # 构建生命周期相关脚本 52 | pre-build: <编译该目标前执行的脚本> 53 | post-build: <编译该目标前之后的脚本> 54 | - name: ... 55 | ... 56 | 57 | ``` 58 | 59 | ### 构建生命周期相关脚本 60 | 61 | `ebuild`将对目标的构建拆分成了若干生命周期,您可以在生命周期中指定执行一些代码。比如编译完成后,您可以使用脚本将输出文件复制到某处,或者对文件创建副本等。 62 | 63 | `ebuild`在执行这些脚本时也会传入一些额外的环境变量,用于在脚本中获得当前构建目标的一些信息。 64 | 65 | 目前的生命周期主要有: 66 | 67 | - pre-build: 在编译目标之前执行 68 | - post-build: 在编译目标之后执行 -------------------------------------------------------------------------------- /src/EBuild/Commands/Base/CommandBase.cs: -------------------------------------------------------------------------------- 1 | using EBuild.Plugins; 2 | using Spectre.Console.Cli; 3 | 4 | namespace EBuild.Commands.Base; 5 | 6 | public class GeneralSettings : CommandSettings 7 | { 8 | } 9 | 10 | public class CommandBase : AsyncCommand 11 | where TSettings : GeneralSettings 12 | { 13 | public CommandBase(IEnumerable plugins) 14 | { 15 | _plugins = plugins; 16 | } 17 | 18 | public TSettings CommandSettings { get; private set; } 19 | public CommandContext CommandContext { get; set; } 20 | protected IEnumerable _plugins; 21 | 22 | public sealed override async Task ExecuteAsync(CommandContext context, TSettings settings) 23 | { 24 | CancellationTokenSource cts = new CancellationTokenSource(); 25 | cts.Token.Register(() => Console.CancelKeyPress -= Handler); 26 | 27 | void Handler(object? sender, ConsoleCancelEventArgs e) 28 | { 29 | e.Cancel = true; 30 | cts.Cancel(); 31 | } 32 | 33 | Console.CancelKeyPress += Handler; 34 | 35 | CommandSettings = settings; 36 | CommandContext = context; 37 | 38 | return await OnExecuteAsync(cts.Token); 39 | } 40 | 41 | public virtual Task OnExecuteAsync(CancellationToken cancellationToken) 42 | { 43 | return Task.FromResult(0); 44 | } 45 | } -------------------------------------------------------------------------------- /src/EBuild/Commands/SubCommands/ToolchainCommand.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | using EBuild.Commands.Base; 3 | using EBuild.Plugins; 4 | using EBuild.Toolchain; 5 | using Spectre.Console; 6 | using YamlDotNet.Serialization; 7 | 8 | namespace EBuild.Commands.SubCommands; 9 | 10 | [Description("检查工具链。")] 11 | public class ToolchainCommand : ProjectCommand 12 | { 13 | private readonly IEnumerable _toolchains; 14 | 15 | public ToolchainCommand(IEnumerable toolchains, IDeserializer deserializer, 16 | IEnumerable plugins) : base(deserializer,plugins) 17 | { 18 | _toolchains = toolchains; 19 | } 20 | 21 | protected override bool ShowLoadConfig() => false; 22 | 23 | protected override int OnExecuteInternal() 24 | { 25 | var table = new Table(); 26 | table.AddColumn("工具"); 27 | table.AddColumn("安装路径"); 28 | table.AddColumn("下载链接"); 29 | 30 | var exitCode = 0; 31 | foreach (var toolchain in _toolchains) 32 | { 33 | toolchain.Search(ProjectRoot); 34 | table.AddRow( 35 | toolchain.Description, 36 | toolchain.Exists() ? toolchain.ExecutablePath : ":cross_mark:", 37 | toolchain.Link 38 | ); 39 | } 40 | 41 | AnsiConsole.Write(table); 42 | return exitCode; 43 | } 44 | } -------------------------------------------------------------------------------- /src/EBuild/Global/Defaults.cs: -------------------------------------------------------------------------------- 1 | using EBuild.Yaml.Converters; 2 | using YamlDotNet.Core; 3 | using YamlDotNet.Serialization; 4 | using YamlDotNet.Serialization.EventEmitters; 5 | using YamlDotNet.Serialization.NamingConventions; 6 | 7 | namespace EBuild.Global; 8 | 9 | internal class LiteralMultilineEventEmitter : ChainedEventEmitter 10 | { 11 | public LiteralMultilineEventEmitter(IEventEmitter nextEmitter) : base(nextEmitter) 12 | { 13 | } 14 | 15 | public override void Emit(ScalarEventInfo eventInfo, IEmitter emitter) 16 | { 17 | if (eventInfo.Source.Type == typeof(string) && eventInfo.Source.Value is string value && value.Contains("\n")) 18 | eventInfo.Style = ScalarStyle.Literal; 19 | 20 | base.Emit(eventInfo, emitter); 21 | } 22 | } 23 | 24 | public static class Defaults 25 | { 26 | public static IDeserializer Deserializer = new DeserializerBuilder() 27 | .WithDefaults() 28 | .Build(); 29 | 30 | public static ISerializer Serializer = new SerializerBuilder() 31 | .WithDefaults() 32 | .WithEventEmitter(e => new LiteralMultilineEventEmitter(e)) 33 | .Build(); 34 | 35 | private static T WithDefaults(this T builder) where T : BuilderSkeleton 36 | { 37 | return builder 38 | .WithNamingConvention(HyphenatedNamingConvention.Instance) 39 | .WithTypeConverter(EnumConverter.Instance); 40 | } 41 | } -------------------------------------------------------------------------------- /src/EBuild.Test/ConfigTests.cs: -------------------------------------------------------------------------------- 1 | using EBuild.Config.Resolved; 2 | using EBuild.Global; 3 | using EBuild.Project; 4 | using EBuild.Sources; 5 | 6 | namespace EBuild.Test; 7 | 8 | public class ConfigTests 9 | { 10 | private static readonly string _projectDir = Path.GetFullPath("./test-project", Directory.GetCurrentDirectory()); 11 | private static string _pwdFilePath = ProjectPath.GetSourcePasswordFilePath(_projectDir); 12 | private ResolvedConfig _resolvedConfig; 13 | 14 | [SetUp] 15 | public void ResolveConfig() 16 | { 17 | _resolvedConfig = ResolvedConfig.Load(_projectDir, Defaults.Deserializer, 18 | PasswordFileResolver.FromProjectRootDir(_projectDir)); 19 | } 20 | 21 | [Test] 22 | public void ResolveTargetsTest() 23 | { 24 | Assert.That(_resolvedConfig.ResolveTargets.Count, Is.EqualTo(4)); 25 | } 26 | 27 | [Test] 28 | public void ExcludeBuildsTest() 29 | { 30 | Assert.That( 31 | _resolvedConfig.ResolveTargets 32 | .Where(x => "not-build".Equals(x.Target.Name)) 33 | .All(x => !x.ShouldBuild) 34 | ); 35 | } 36 | 37 | [Test] 38 | public void PasswordResolveTest() 39 | { 40 | Assert.That( 41 | _resolvedConfig.ResolveTargets 42 | .Where(x => !string.IsNullOrEmpty(x.Password)) 43 | .All(x => "12345".Equals(x.Password)) 44 | ); 45 | } 46 | } -------------------------------------------------------------------------------- /src/EBuild/Consoles/TableDisplayer.cs: -------------------------------------------------------------------------------- 1 | using Spectre.Console; 2 | 3 | namespace EBuild.Consoles; 4 | 5 | public class TableDisplayer : MultiTaskDisplayer 6 | { 7 | private readonly IList _targets; 8 | private LiveDisplayContext _ctx; 9 | private Table _table; 10 | 11 | public TableDisplayer(IList targets) 12 | { 13 | _targets = targets; 14 | } 15 | 16 | public override async Task StartAsync(Func, Task> action) 17 | { 18 | _table = new Table(); 19 | _table.AddColumns("状态", "目标", "日志"); 20 | foreach (var target in _targets) 21 | _table.AddRow("", Markup.Escape(TargetName(target)), ""); 22 | 23 | TR? res = default; 24 | await AnsiConsole.Live(_table) 25 | .StartAsync(async ctx => 26 | { 27 | _ctx = ctx; 28 | res = await action(this); 29 | }); 30 | return res; 31 | } 32 | 33 | public override void SetHeader(string header) 34 | { 35 | _table.Title(header); 36 | _ctx.Refresh(); 37 | } 38 | 39 | public override void Update(TTarget target, TTargetStatus status, string log) 40 | { 41 | int id = _targets.IndexOf(target); 42 | _table.UpdateCell(id, 0, StatusString(status)); 43 | _table.UpdateCell(id, 2, log); 44 | _ctx.Refresh(); 45 | } 46 | } -------------------------------------------------------------------------------- /src/EBuild/Sources/PasswordResolver.cs: -------------------------------------------------------------------------------- 1 | using YamlDotNet.Serialization; 2 | 3 | namespace EBuild.Sources; 4 | 5 | public interface IPasswordResolver 6 | { 7 | string Resolve(string source); 8 | } 9 | 10 | public class PasswordFileResolver : IPasswordResolver 11 | { 12 | private readonly string _projectRoot; 13 | private readonly string _pwdFilePath; 14 | private Dictionary? _pwdDict; 15 | 16 | public PasswordFileResolver(string passwordFilePath, string projectRoot) 17 | { 18 | _pwdFilePath = passwordFilePath; 19 | _projectRoot = projectRoot; 20 | } 21 | 22 | public string Resolve(string source) 23 | { 24 | if (_pwdDict == null) 25 | { 26 | _pwdDict = new Dictionary(); 27 | try 28 | { 29 | var original = new Deserializer().Deserialize>(File.OpenText(_pwdFilePath)); 30 | foreach (var (file, pwd) in original) _pwdDict[Path.GetFullPath(file, _projectRoot)] = pwd; 31 | } 32 | catch (Exception e) 33 | { 34 | // ignored 35 | } 36 | } 37 | 38 | if (_pwdDict.ContainsKey(source)) 39 | return _pwdDict[source]; 40 | return string.Empty; 41 | } 42 | 43 | public static PasswordFileResolver FromProjectRootDir(string project) 44 | { 45 | return new PasswordFileResolver(Path.GetFullPath("./ebuild.pwd.yaml", project), project); 46 | } 47 | } -------------------------------------------------------------------------------- /ebuild.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30114.105 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EBuild", "src\EBuild\EBuild.csproj", "{3AD6A129-0773-4DAE-8B4E-95FAF615D4AB}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EBuild.Test", "src\EBuild.Test\EBuild.Test.csproj", "{0016EE03-A7BA-4E74-A1E9-4045FB4BD7BC}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(SolutionProperties) = preSolution 16 | HideSolutionNode = FALSE 17 | EndGlobalSection 18 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 19 | {3AD6A129-0773-4DAE-8B4E-95FAF615D4AB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 20 | {3AD6A129-0773-4DAE-8B4E-95FAF615D4AB}.Debug|Any CPU.Build.0 = Debug|Any CPU 21 | {3AD6A129-0773-4DAE-8B4E-95FAF615D4AB}.Release|Any CPU.ActiveCfg = Release|Any CPU 22 | {3AD6A129-0773-4DAE-8B4E-95FAF615D4AB}.Release|Any CPU.Build.0 = Release|Any CPU 23 | {0016EE03-A7BA-4E74-A1E9-4045FB4BD7BC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 24 | {0016EE03-A7BA-4E74-A1E9-4045FB4BD7BC}.Debug|Any CPU.Build.0 = Debug|Any CPU 25 | {0016EE03-A7BA-4E74-A1E9-4045FB4BD7BC}.Release|Any CPU.ActiveCfg = Release|Any CPU 26 | {0016EE03-A7BA-4E74-A1E9-4045FB4BD7BC}.Release|Any CPU.Build.0 = Release|Any CPU 27 | EndGlobalSection 28 | EndGlobal 29 | -------------------------------------------------------------------------------- /.github/workflows/changelog.yml: -------------------------------------------------------------------------------- 1 | name: Changelog 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | 7 | jobs: 8 | build: 9 | name: "✏️ Changelog generation" 10 | runs-on: ubuntu-18.04 11 | steps: 12 | - name: "📥 Check-out" 13 | uses: actions/checkout@v2 14 | - name: "✏️ Generate full changelog" 15 | id: generate-full-changelog 16 | uses: heinrichreimer/github-changelog-generator-action@v2.3 17 | with: 18 | token: ${{ secrets.GITHUB_TOKEN }} 19 | headerLabel: "# 📑 更新日志" 20 | breakingLabel: '### 💥 破坏性变更' 21 | enhancementLabel: '### 🚀 增强' 22 | bugsLabel: '### 🐛 BUG修复' 23 | deprecatedLabel: '### ⚠️ 弃用' 24 | removedLabel: '### 🔥 移除' 25 | securityLabel: '### 🛡️ 安全' 26 | issuesLabel: '### 📁 Issues' 27 | prLabel: '### 📁 Pull requests' 28 | addSections: '{"documentation":{"prefix":"### 📖 文档","labels":["documentation"]},"tests":{"prefix":"### ✅ 测试","labels":["tests"]}}' 29 | issues: true 30 | issuesWoLabels: true 31 | pullRequests: true 32 | prWoLabels: true 33 | author: true 34 | unreleased: true 35 | compareLink: true 36 | stripGeneratorNotice: true 37 | verbose: true 38 | - name: "🖨️ Print changelog to console" 39 | run: cat CHANGELOG.md 40 | - name: "📤 Upload changelog" 41 | uses: actions/upload-artifact@v1.0.0 42 | with: 43 | name: "Changelog" 44 | path: CHANGELOG.md 45 | -------------------------------------------------------------------------------- /src/EBuild/Yaml/Converters/EnumConverter.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using YamlDotNet.Core; 3 | using YamlDotNet.Core.Events; 4 | using YamlDotNet.Serialization; 5 | using YamlDotNet.Serialization.NamingConventions; 6 | 7 | namespace EBuild.Yaml.Converters; 8 | 9 | public class EnumConverter : IYamlTypeConverter 10 | { 11 | public static readonly EnumConverter Instance = new EnumConverter(); 12 | 13 | public bool Accepts(Type type) 14 | { 15 | return type.IsEnum; 16 | } 17 | 18 | public object? ReadYaml(IParser parser, Type type) 19 | { 20 | var possibleNames = new HashSet(); 21 | var scalar = parser.Consume(); 22 | possibleNames.Add(scalar.Value); 23 | possibleNames.Add(PascalCaseNamingConvention.Instance.Apply(scalar.Value)); 24 | 25 | foreach (var value in Enum.GetValues(type)) 26 | { 27 | var alias = type.GetField(Enum.GetName(type, value)!)!.GetCustomAttribute(); 28 | if (alias != null && possibleNames.Contains(alias.Name)) return value; 29 | } 30 | 31 | foreach (var possibleName in possibleNames) 32 | if (Enum.TryParse(type, possibleName, out var result)) 33 | return result; 34 | 35 | return null; 36 | } 37 | 38 | public void WriteYaml(IEmitter emitter, object? value, Type type) 39 | { 40 | var alias = type.GetCustomAttribute(); 41 | emitter.Emit(alias != null ? new Scalar(alias.Name) : new Scalar(value?.ToString() ?? "")); 42 | } 43 | } -------------------------------------------------------------------------------- /src/EBuild.Test/ToolchainTests.cs: -------------------------------------------------------------------------------- 1 | using EBuild.Config; 2 | using EBuild.Toolchain; 3 | 4 | namespace EBuild.Test; 5 | 6 | internal class ToolchainTests 7 | { 8 | private readonly E2TxtToolchain _e2txt; 9 | 10 | [Test] 11 | public void E2TxtArgsTest() 12 | { 13 | var source = "esrc!!!!!!!!!!!!!"; 14 | var ecode = "ecode!!!!!!!!!!!!!"; 15 | E2Txt config = new E2Txt { GenerateE = true, NameStyle = E2Txt.NameStyleEnum.Chinese }; 16 | CollectionAssert.AreEquivalent( 17 | new List { "-ns", "2", "-e", "-log", "-enc", "UTF-8" }, 18 | E2TxtToolchain.GeneralArgs(config)); 19 | 20 | var e2TxtArgs = E2TxtToolchain.E2TxtArgs(source, ecode, config); 21 | CollectionAssert.AreEquivalent( 22 | new List 23 | { "-ns", "2", "-e", "-log", "-enc", "UTF-8", "-src", source, "-dst", ecode, "-mode", "e2t" }, 24 | e2TxtArgs); 25 | Assert.That(e2TxtArgs[e2TxtArgs.IndexOf("-src") + 1], Is.EqualTo(source)); 26 | Assert.That(e2TxtArgs[e2TxtArgs.IndexOf("-dst") + 1], Is.EqualTo(ecode)); 27 | 28 | var txt2EArgs = E2TxtToolchain.Txt2EArgs(ecode, source, config); 29 | CollectionAssert.AreEquivalent( 30 | new List 31 | { "-ns", "2", "-e", "-log", "-enc", "UTF-8", "-src", ecode, "-dst", source, "-mode", "t2e" }, 32 | txt2EArgs); 33 | Assert.That(txt2EArgs[txt2EArgs.IndexOf("-src") + 1], Is.EqualTo(ecode)); 34 | Assert.That(txt2EArgs[txt2EArgs.IndexOf("-dst") + 1], Is.EqualTo(source)); 35 | } 36 | } -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build Latest 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: ["master"] 7 | paths: ["./src/**"] 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | strategy: 13 | matrix: 14 | sc: ["--self-contained"] 15 | single-file: ["true", "false"] 16 | runtime: ["win-x86"] 17 | steps: 18 | 19 | - uses: actions/checkout@v3 20 | 21 | - name: Setup .NET 22 | uses: actions/setup-dotnet@v2 23 | with: 24 | dotnet-version: 6.0.x 25 | 26 | - name: Restore dependencies 27 | run: dotnet restore --runtime ${{ matrix.runtime }} 28 | 29 | - name: Build 30 | run: dotnet build --no-restore 31 | 32 | - name: Test 33 | run: dotnet test --no-build --verbosity normal 34 | 35 | - name: Publish 36 | run: dotnet publish ./src/EBuild/ --runtime ${{ matrix.runtime }} --configuration Release ${{ matrix.sc }} --no-restore -p:PublishSingleFile=${{ matrix.single-file }} -p:PublishTrimmed=true 37 | 38 | - name: Upload Artifact 39 | if: matrix.single-file == 'true' 40 | uses: actions/upload-artifact@v1.0.0 41 | with: 42 | name: ebuild-${{ matrix.runtime }}-single-file 43 | path: ./src/EBuild/bin/Release/net6.0/${{ matrix.runtime }}/publish 44 | 45 | - name: Upload Artifact 46 | if: matrix.single-file == 'false' 47 | uses: actions/upload-artifact@v1.0.0 48 | with: 49 | name: ebuild-${{ matrix.runtime }} 50 | path: ./src/EBuild/bin/Release/net6.0/${{ matrix.runtime }}/publish 51 | -------------------------------------------------------------------------------- /src/EBuild.Test/EBuild.Test.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | enable 6 | enable 7 | 8 | false 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | Always 26 | 27 | 28 | Always 29 | 30 | 31 | Always 32 | 33 | 34 | Always 35 | 36 | 37 | Always 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /docs/project/basic.md: -------------------------------------------------------------------------------- 1 | # 基本配置 2 | 3 | ## 工程描述信息 4 | 5 | ```yaml 6 | project: 7 | name: ebuild-example # 工程名 8 | version: "1.0" # 工程版本 9 | description: 这是由ebuild创建的示例工程。 # 工程的描述信息 10 | author: SalHe # 工程作者 11 | repository: https://github.com/SalHe/ebuild # 工程的源码仓库地址 12 | homepage: https://github.com/SalHe # 工程主页 13 | ``` 14 | 15 | 您可以在`ebuild.yaml`中看到上述的内容,在`project`之下有若干用于描述工程的信息,他们对工程没有任何影响,仅仅是用于描述工程,方便了解工程的信息。 16 | 17 | 18 | ## 管理源码 19 | 20 | 在`ebuild`中,并不是所有源码都会参与`e2txt`、`txt2e`、编译等的过程,只有那些按照约定包含(includes)进来但又没有被排除掉的文件(excludes)的文件才能参与这些过程。 21 | 22 | ### 被管理的源文件 23 | 24 | ```yaml 25 | project: 26 | # ...... 27 | includes: 28 | - '**/*.e' 29 | ``` 30 | 31 | 在`includes`节点中,您可以配置多种文件名匹配模式来选择你需要被纳入控制的源码。在上述配置中,要求工程管理所有目录(`**/`)下的易语言源文件(`*.e`)。 32 | 33 | ### 排除的源文件 34 | 35 | 有时候,您希望在工程中排除一些源文件,使他们不纳入`ebuild`的控制——既不参与`e2txt`也不参与编译。那么您可以配置`excludes`。 36 | 37 | ```yaml 38 | project: 39 | # ...... 40 | excludes: 41 | - '**/*.recover.e' 42 | - '**/*.ecode/*.e' 43 | - '**/*.代码/*.e' 44 | ``` 45 | 46 | 上述代码中,排除了三种模式的文件,比如`XXX.recover.e`被排除掉了。 47 | 48 | `excludes`的优先级是高于`includes`的,即:纵使`includes`中包含了`XXX.recover.e`,他也不会被纳入`ebuild`的控制。 49 | 50 | ### 仅在构建中排除源文件 51 | 52 | ```yaml 53 | project: 54 | # ...... 55 | exclude-builds: 56 | - './scripts/**/*.e' # 脚本文件不纳入'ebuild build'命令中进行自动构建 57 | ``` 58 | 59 | 与`excludes`不同,有些源文件您希望参与`e2txt`,但不希望他作为编译目标——作为脚本的易语言源文件便是一个例子,上述配置使得`scripts`目录下的所有易语言源文件`*.e`都不参与编译,但是他们仍然参与`e2txt`。 60 | 61 | `exclude-builds`的优先级高于`includes`,低于`excludes`,即:倘若`excludes`排除了指定源文件,即便该配置中未排除那个文件,该文件最终仍然被排除(既不参与`e2txt`也不参与编译)。 -------------------------------------------------------------------------------- /src/EBuild/Sources/ESourceMeta.cs: -------------------------------------------------------------------------------- 1 | using EBuild.Yaml.Converters; 2 | 3 | namespace EBuild.Sources; 4 | 5 | public enum SourceType 6 | { 7 | [EnumAlias("源码")] Src = 1, 8 | [EnumAlias("模块")] ECom = 3, 9 | } 10 | 11 | public enum TargetType 12 | { 13 | [ESourceTargetType(Extension = "exe")] WinForm = 0, 14 | [ESourceTargetType(Extension = "exe")] WinConsole = 1, 15 | [ESourceTargetType(Extension = "dll")] WinDll = 2, 16 | [ESourceTargetType(Extension = "ec")] WinEc = 1000, 17 | [ESourceTargetType(Extension = null)] LinuxConsole = 10000, 18 | [ESourceTargetType(Extension = null)] LinuxEc = 11000 19 | } 20 | 21 | public class ESourceMeta 22 | { 23 | public SourceType SourceType { get; set; } 24 | public TargetType TargetType { get; set; } 25 | 26 | private ESourceMeta() 27 | { 28 | } 29 | 30 | public static ESourceMeta? FromSource(string sourcePath) 31 | { 32 | try 33 | { 34 | using var fs = File.OpenRead(sourcePath); 35 | using var br = new BinaryReader(fs); 36 | fs.Seek(124L, SeekOrigin.Begin); 37 | var st = (SourceType)br.ReadInt32(); 38 | fs.Seek(132L, SeekOrigin.Begin); 39 | var tt = (TargetType)br.ReadInt32(); 40 | return new ESourceMeta() 41 | { 42 | SourceType = st, 43 | TargetType = tt, 44 | }; 45 | } 46 | catch (Exception e) 47 | { 48 | return null; 49 | } 50 | } 51 | } 52 | 53 | [AttributeUsage(AttributeTargets.Field)] 54 | public class ESourceTargetTypeAttribute : Attribute 55 | { 56 | public string? Extension { get; set; } 57 | } -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # EBuild 2 | 3 | `ebuild`是一个针对易语言的构建工具,该工具主要使用配置化的方式完成自动化的将易语言源文件转换成文本格式描述的文件(使用了[e2txt](http://e2ee.jimstone.com.cn/downloads/)) 4 | ,以及借助[ecl](https://bbs.125.la/forum.php?mod=viewthread&tid=14553929&highlight=ecl)完成源文件编译。 5 | 6 | 目前,`ebuild`已不再使用`Go`语言开发,转而使用`C#`基于`.NET6`开发,关于这样做的原因请参见:[使用C#+.NET6重写ebuild,为后续开发打基础](https://github.com/SalHe/ebuild/pull/1)。 7 | 8 | ## 特性 9 | 10 | - [x] 可根据需要选择需要完成自动化管理的易语言源文件 11 | - [x] 可排除特定源文件 12 | - [x] 批量完成e2txt/txt2e 13 | - 全转换 14 | - 指定转换目标 15 | - [x] 文件清理 16 | - *.recover.e 使用`txt2e`从文本格式代码恢复出来的易语言二进制源文件 17 | - *.ecode 使用`e2txt`从易语言二进制源文件转换出来的文本格式代码 18 | - ebuild-out 中构建生成的文件 19 | - [ ] 检测源文件变化,并自动转换成文本格式的代码 20 | - [x] 批量构建目标 21 | - 全构建 22 | - 指定构建目标 23 | - [x] 构建前后动作 24 | - [x] 立即编译并执行易语言源文件 25 | - [ ] 使用易语言程序作为当前项目`ebuild`插件,以参与程序编译过程 26 | 27 | ## 命令行帮助 28 | 29 | 请见[命令行帮助](./cli/) 30 | 31 | ## 效果图 32 | 33 | ### 预览项目信息 34 | 35 | ```shell 36 | ./ebuild.exe info --project ./example/ 37 | ``` 38 | 39 | ![查看项目信息](./imgs/info.gif) 40 | 41 | ### e2txt/txt2e 42 | 43 | ```shell 44 | ./ebuild.exe e2txt --project ./example/ 45 | ./ebuild.exe txt2e --project ./example/ 46 | ``` 47 | 48 | ![e2txt](./imgs/e2txt.gif) 49 | ![txt2e](./imgs/txt2e.gif) 50 | 51 | ### 构建 52 | 53 | ```shell 54 | ./ebuild.exe build --project ./example/ 55 | ``` 56 | 57 | ![构建项目](./imgs/build.gif) 58 | 59 | ## 引用项目 60 | 61 | 该项目的存在离不开以下作者和项目为易语言生态带来的贡献: 62 | 63 | - [e2txt](http://e2ee.jimstone.com.cn/) by [JimStone](http://e2ee.jimstone.com.cn/) 64 | - [易语言命令行编译工具 ecl](https://bbs.125.la/forum.php?mod=viewthread&tid=14553929&highlight=ecl) 65 | by [被封七号](https://bbs.125.la/home.php?mod=space&uid=504218&do=thread&type=thread&view=me&from=space) 66 | 67 | > 如有任何形式的对于作者或项目的侵犯行为,请见谅,并联系我对项目做出调整或者下架。 68 | -------------------------------------------------------------------------------- /docs/first-project.md: -------------------------------------------------------------------------------- 1 | # 第一个工程 2 | 3 | 在安装好`ebuild`及相关工具之后,您便可以享受由`ebuild`为您带来的好处了。 4 | 5 | ## 初始化工程 6 | 7 | 新建一个文件夹如`ebuild工程`,打开命令行并进入到该文件夹中,执行: 8 | 9 | ```shell 10 | ebuild init 11 | ``` 12 | 13 | 执行完成后,您可以看到以下提示,同时`ebuild`将为您创建与工程相关的一些文件。 14 | 15 | ![ebuild_init](imgs/ebuild_init.png) 16 | 17 | ## 工程文件 18 | 19 | ``` 20 | ebuild-project 21 | ├── .gitignore # git将忽略的文件列表 22 | ├── ebuild.pwd.yaml # ebuild中源码的密码(如果有密码的话) 23 | ├── ebuild.yaml # ebuild工程配置文件,描述了工程自身的信息、包含的源码、源码的编译方式等。 24 | └── README.md 25 | ``` 26 | 27 | ## 添加源码 28 | 29 | 现在您可以在该目录下创建易语言源码。 30 | 31 | 比如在工程目录下创建一个`Windows控制台程序.e`: 32 | 33 | ``` 34 | .版本 2 35 | 36 | .程序集 程序集1 37 | 38 | .子程序 _启动子程序, 整数型, , 本子程序在程序启动后最先执行 39 | 40 | 41 | 标准输出 (, “你好,ebuild!”) 42 | 43 | 返回 (0) ' 可以根据您的需要返回任意数值 44 | ``` 45 | 46 | ## `e2txt` 47 | 48 | 接下来,您便可以使用`ebuild e2txt`将工程中包含的源码借助`e2txt`将易语言源码转换成文本格式的代码,这样可以方便您使用`git`来管理您的代码。 49 | 50 | ![e2txt](./imgs/fp/e2txt.gif) 51 | 52 | 完成上述转换后,您可以在`<工程根目录>\Windows控制台程序.ecode`看到您的文本格式的代码。 53 | 54 | ## `txt2e` 55 | 56 | 同时也可以使用`ebuild txt2e`将工程中的文本格式的代码恢复为易语言源文件(该恢复过程只针对被包含在工程中的源文件对应的文本格式代码),此外恢复的源文件的文件名中将加入`.recover`。比如,`<工程根目录>\Windows控制台程序.ecode`恢复后,您将得到恢复后的源文件``<工程根目录>\Windows控制台程序.recover.e`。 57 | 58 | ::: info 59 | 由于`e2txt`和`txt2e`并不是一个完全逆向的过程,`ebuild`不会主动将您原本的源文件替换为恢复的源文件`*.recover.e`,这是出于对您数据的安全性考虑。 60 | ::: 61 | 62 | ## 构建工程 63 | 64 | 使用`ebuild build`您便可以将工程中的所有源文件根据配置完成编译了。编译后的文件目前会输出到`<工程根目录>\ebuild-out\`中,后续版本可能考虑允许您自定义输出目录。 65 | 66 | ![build](./imgs/fp/build.png) 67 | 68 | 在上述的输出结果中,我们可以看到编译后的文件输出到`C:\Users\SalHe\Desktop\ebuild-project\ebuild-out\Windows控制台程序.exe`去了。 69 | 70 | ## 清理工程 71 | 72 | 使用`ebuild clean`您可以清理`ebuild`生成的中间文件。 73 | 74 | ![clean](./imgs/fp/clean.png) 75 | 76 | ## 最后 77 | 78 | 这里只是简单说明`ebuild`的使用,关于配置文件的详细细节和命令行的相关参数等将在其他文档中进行讨论。 79 | 80 | 本文中的工程可以参见 https://github.com/SalHe/ebuild/blob/master/examples/first-project 。 -------------------------------------------------------------------------------- /src/EBuild/Toolchain/E2TxtToolchain.cs: -------------------------------------------------------------------------------- 1 | using EBuild.Config; 2 | 3 | namespace EBuild.Toolchain; 4 | 5 | public class E2TxtToolchain : GeneralToolchain 6 | { 7 | public override IList EnvironmentVariables => new List() 8 | { 9 | new("E2TXT_DIR", "e2txt安装路径", () => Path.GetDirectoryName(ExecutablePath) ?? ""), 10 | }; 11 | 12 | public E2TxtToolchain() : base("e2txt", "http://e2ee.jimstone.com.cn/downloads/", "e2txt") 13 | { 14 | } 15 | 16 | public static IList GeneralArgs(E2Txt e2txtConfig) 17 | { 18 | var args = new List() { "-log", "-enc", "UTF-8" }; 19 | 20 | switch (e2txtConfig.NameStyle) 21 | { 22 | case E2Txt.NameStyleEnum.English: 23 | args.Add("-ns"); 24 | args.Add("1"); 25 | break; 26 | case E2Txt.NameStyleEnum.Chinese: 27 | args.Add("-ns"); 28 | args.Add("2"); 29 | break; 30 | } 31 | 32 | if (e2txtConfig.GenerateE) 33 | args.Add("-e"); 34 | 35 | return args; 36 | } 37 | 38 | private static IList Args(string from, string to, string mode, E2Txt e2txtCofig) 39 | { 40 | var args = GeneralArgs(e2txtCofig); 41 | args.Add("-src"); 42 | args.Add(from); 43 | args.Add("-dst"); 44 | args.Add(to); 45 | args.Add("-mode"); 46 | args.Add(mode); 47 | return args; 48 | } 49 | 50 | public static IList E2TxtArgs(string source, string ecodeDir, E2Txt e2txtCofig) 51 | { 52 | return Args(source, ecodeDir, "e2t", e2txtCofig); 53 | } 54 | 55 | public static IList Txt2EArgs(string ecodeDir, string source, E2Txt e2txtCofig) 56 | { 57 | return Args(ecodeDir, source, "t2e", e2txtCofig); 58 | } 59 | } -------------------------------------------------------------------------------- /src/EBuild/Toolchain/GeneralToolchain.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | 3 | namespace EBuild.Toolchain; 4 | 5 | public abstract class GeneralToolchain : IToolchain 6 | { 7 | private readonly string _executableName; 8 | 9 | public GeneralToolchain(string description, string link, string executableName) 10 | { 11 | _executableName = executableName; 12 | Description = description; 13 | Link = link; 14 | } 15 | 16 | public string Description { get; } 17 | public string Link { get; } 18 | public string ExecutablePath { get; protected set; } = string.Empty; 19 | public abstract IList EnvironmentVariables { get; } 20 | 21 | public virtual void Search(string projectRootDir) 22 | { 23 | var possible = new List() 24 | { 25 | GetExecutablePath(projectRootDir, _executableName), 26 | GetExecutablePath(Directory.GetCurrentDirectory(), _executableName), 27 | GetExecutablePath(Path.GetDirectoryName(Process.GetCurrentProcess().MainModule?.FileName ?? "") ?? "", 28 | _executableName) 29 | }; 30 | foreach (var path in possible) 31 | if (File.Exists(path)) 32 | { 33 | ExecutablePath = path; 34 | return; 35 | } 36 | 37 | ExecutablePath = Environment.GetEnvironmentVariable("PATH")! 38 | .Split(";") 39 | .Where(s => File.Exists(Path.Combine(s, _executableName + ".exe"))) 40 | .FirstOrDefault(string.Empty); 41 | } 42 | 43 | public bool Exists() 44 | { 45 | return !string.IsNullOrEmpty(ExecutablePath) && File.Exists(ExecutablePath); 46 | } 47 | 48 | private string GetExecutablePath(string root, string name) 49 | { 50 | return Path.GetFullPath(Path.Join(root, ".toolchain", name, name + ".exe")); 51 | } 52 | } -------------------------------------------------------------------------------- /examples/simple/ebuild.yaml: -------------------------------------------------------------------------------- 1 | project: 2 | name: ebuild-example 3 | version: "1.0" 4 | description: 这是由ebuild创建的示例工程。 5 | author: SalHe 6 | repository: https://github.com/SalHe/ebuild 7 | homepage: https://github.com/SalHe 8 | scripts: 9 | show-envs: | 10 | @echo off 11 | echo EBuild="%EBUILD_EXECUTABLE_PATH%" 12 | echo 易语言="%ELANG_DIR%" 13 | echo Ecl="%ECL_DIR%" 14 | echo E2Txt="%E2Txt_DIR%" 15 | get-input: | 16 | @echo off 17 | @REM 演示如何获取用户输入 18 | 19 | set /p Username=用户名: 20 | set /p Password=密码: 21 | 22 | echo/ 23 | echo 您的用户名:%Username% 24 | echo 您的密码:%Password% 25 | cmd-args: | 26 | @echo off 27 | echo arg0=%0 28 | echo arg1=%1 29 | echo arg2=%2 30 | echo arg3=%3 31 | excludes: 32 | - '**/*.recover.e' 33 | - '**/*.ecode/**.e' 34 | - '**/*.代码/**.e' 35 | exclude-builds: 36 | - './scripts/**/*.e' # 脚本文件不纳入'ebuild build'命令中进行自动构建 37 | includes: 38 | - '**/*.e' 39 | e2txt: 40 | name-style: 中文 41 | generate-e: true 42 | build: 43 | compiler: 独立编译 44 | targets: 45 | - name: Windows窗口程序示例 46 | description: 这是一个特别简单的窗口程序,里面有个按钮,你点击后会弹出提示。 47 | source: ./Windows窗口程序.e # 相对于项目根路径 48 | # 因为没有为该目标指定编译方式,所以会默认采用工程配置'build.compiler'中的编译方式 49 | output: Windows窗口程序示例.exe 50 | package: false # 不是易包 51 | hooks: 52 | pre-build: | 53 | @echo off 54 | echo 当前时期:%EBUILD_PERIOD% 55 | echo 源文件:%EBUILD_SOURCE_FILE% 56 | echo 目标文件:%EBUILD_TARGET_FILE% 57 | echo 目标类型:%EBUILD_TARGET_TYPE% 58 | post-build: | 59 | @echo off 60 | copy "%EBUILD_TARGET_FILE%" "%EBUILD_TARGET_FILE%.copy" 61 | 62 | - name: Windows控制台程序——静态编译版 63 | description: 这是一个简单的控制台程序,会在标准输出输出一句问候。 64 | source: ./Windows控制台程序.e 65 | output: Windows控制台程序示例——静态编译版.exe 66 | build: 67 | compiler: 静态编译 68 | 69 | - name: Windows控制台程序——黑月版 70 | description: 这是一个简单的控制台程序,会在标准输出输出一句问候。 71 | source: ./Windows控制台程序.e 72 | output: Windows控制台程序示例——黑月版.exe 73 | build: 74 | compiler: 黑月编译 75 | -------------------------------------------------------------------------------- /docs/.vitepress/config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vitepress"; 2 | import miCheckbox from "markdown-it-task-checkbox"; 3 | import { version } from '../../package.json'; 4 | 5 | export default defineConfig({ 6 | lang: 'zh-CN', 7 | base: '/ebuild/', 8 | title: 'EBuild', 9 | description: 'EBuild 是一个专注于易语言的构建工具。', 10 | lastUpdated: true, 11 | themeConfig: { 12 | outlineTitle: '目录', 13 | footer: { 14 | message: '本开源软件受 MIT 协议保护', 15 | copyright: 'Copyright (c) 2022 SalHe Li' 16 | }, 17 | 18 | nav: [ 19 | { text: '使用向导', link: '/README' }, 20 | { text: '命令行帮助', link: '/cli/' }, 21 | { text: `v${version}`, link: `https://github.com/SalHe/ebuild/releases/tag/v${version}` }, 22 | { 23 | text: '友情链接', 24 | items: [ 25 | { text: "SalHe's Home", link: "https://salhe.github.io" }, 26 | { text: "SalHe's Blog", link: "https://salhe.github.io/blog" } 27 | ] 28 | } 29 | ], 30 | sidebar: [ 31 | { 32 | text: '介绍', 33 | items: [ 34 | { text: '什么是 ebuild?', link: '/README' }, 35 | { text: '安装', link: '/installation' }, 36 | { text: '第一个工程', link: '/first-project' }, 37 | { text: '鸣谢', link: '/thanks' }, 38 | ] 39 | }, 40 | { 41 | text: '工程配置', 42 | items: [ 43 | { text: '基本配置', link: '/project/basic' }, 44 | { text: '工程脚本 - 重复工作', link: '/project/run' }, 45 | { text: 'e2txt配置', link: '/project/e2txt' }, 46 | { text: '构建配置', link: '/project/build' }, 47 | { text: '环境变量', link: '/project/environ' }, 48 | { text: '案例', link: '/project/examples' }, 49 | ] 50 | }, 51 | { 52 | text: '命令行帮助', 53 | items: [ 54 | { text: '概览', link: '/cli/' }, 55 | ] 56 | } 57 | ], 58 | editLink: { 59 | pattern: 'https://github.com/SalHe/ebuild/edit/master/docs/:path', 60 | text: '在 GitHub 上编辑' 61 | }, 62 | socialLinks: [ 63 | { icon: 'github', link: 'https://github.com/SalHe/ebuild' }, 64 | ], 65 | algolia: { 66 | appId: 'I0YZBJE735', 67 | apiKey: 'be48f498e8c14176450fe574c8347f12', 68 | indexName: 'ebuild' 69 | }, 70 | }, 71 | markdown: { 72 | config: (md) => { 73 | md.use(miCheckbox, { readonly: true }) 74 | } 75 | } 76 | }) 77 | -------------------------------------------------------------------------------- /src/EBuild/Plugins/BuildHookScriptPlugin.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using System.Text; 3 | using EBuild.Commands.Base; 4 | using EBuild.Hooks; 5 | using Spectre.Console; 6 | 7 | namespace EBuild.Plugins; 8 | 9 | public class BuildHookScriptPlugin : Plugin 10 | { 11 | public override async Task OnHook(PluginContext context, CancellationToken cancellationToken, Hook hook) 12 | { 13 | if (context.BuildTarget!.Target?.Hooks?.ContainsKey(hook) != true) return true; 14 | string batContent = context.BuildTarget!.Target?.Hooks?[hook].ReplaceLineEndings("\r\n") ?? ""; 15 | if (string.IsNullOrEmpty(batContent)) 16 | return true; 17 | 18 | var tempFile = Path.GetTempFileName(); 19 | var tempBatFile = Path.ChangeExtension(tempFile, "ebuild-hooks.bat"); 20 | File.Move(tempFile, tempBatFile); 21 | await File.WriteAllTextAsync(tempBatFile, batContent, cancellationToken); 22 | 23 | context.UpdateStatus?.Invoke(TargetStatus.Doing, $"正在准备执行编译周期脚本: {tempBatFile.EscapeMarkup()}"); 24 | 25 | var process = new Process(); 26 | context.EnvironmentVariables.LoadToStringDictionary(process.StartInfo.EnvironmentVariables); 27 | process.StartInfo.FileName = tempBatFile; 28 | 29 | process.StartInfo.StandardOutputEncoding = 30 | process.StartInfo.StandardErrorEncoding = Encoding.UTF8; 31 | process.StartInfo.RedirectStandardOutput = process.StartInfo.RedirectStandardError = true; 32 | process.StartInfo.RedirectStandardInput = true; 33 | 34 | void Handler(object sender, DataReceivedEventArgs e) 35 | { 36 | if (!string.IsNullOrEmpty(e.Data)) 37 | context.UpdateStatus?.Invoke(TargetStatus.Doing, e.Data); 38 | } 39 | 40 | process.OutputDataReceived += Handler; 41 | process.ErrorDataReceived += Handler; 42 | 43 | cancellationToken.Register(() => 44 | { 45 | process.CancelErrorRead(); 46 | process.CancelOutputRead(); 47 | process.Kill(); 48 | }); 49 | process.Start(); 50 | process.BeginOutputReadLine(); 51 | process.BeginErrorReadLine(); 52 | await process.WaitForExitAsync(cancellationToken); 53 | 54 | // 思考了一下还是决定不允许在构建脚本中接收输入(标准输入),尽管之前golang版本中已实现 55 | 56 | context.UpdateStatus?.Invoke(TargetStatus.Doing, $"脚本退出代码:{process.ExitCode}"); 57 | 58 | File.Delete(tempBatFile); 59 | 60 | return process.ExitCode == 0; 61 | } 62 | } -------------------------------------------------------------------------------- /src/EBuild/Commands/SubCommands/CleanCommand.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | using EBuild.Commands.Base; 3 | using EBuild.Plugins; 4 | using EBuild.Project.Cleaners; 5 | using Spectre.Console; 6 | using Spectre.Console.Cli; 7 | using YamlDotNet.Serialization; 8 | 9 | namespace EBuild.Commands.SubCommands; 10 | 11 | [Description(@"清理恢复的源码等工程中间文件。 12 | 该命令可以清理恢复的源码等工程中间文件。包括: 13 | 14 | 1. txt2e 中从文本格式代码恢复出来的易语言源文件(*.recover.e,只清理包含在工程源文件和目标中的恢复代码); 15 | 2. e2txt 中从易语言源文件转换成的文本格式代码的文件夹(*.ecode,只清理包含在工程源文件和目标中的恢复代码)。(可选,默认不清理) 16 | 3. build 中构建出来的目标文件(实际会清理构建输出文件夹中的所有文件)。 17 | ")] 18 | public class CleanCommand : ProjectCommand 19 | { 20 | public class Settings : ProjectSettings 21 | { 22 | [CommandOption("--ecode")] 23 | [Description("清理 .ecode 文件夹")] 24 | public bool CleanECode { get; init; } = false; 25 | 26 | [CommandOption("-f|--force")] 27 | [Description("强制同意。针对危险的操作,ebuild会尝试询问您是否确定,但是如果您开启了此标志,则ebuild认为您坚持对所有危险操作继续执行。")] 28 | public bool ForceClean { get; init; } = false; 29 | } 30 | 31 | private readonly IEnumerable _projectCleaners; 32 | 33 | public CleanCommand(IDeserializer deserializer, IEnumerable projectCleaners, 34 | IEnumerable plugins) : base(deserializer,plugins) 35 | { 36 | _projectCleaners = projectCleaners; 37 | } 38 | 39 | protected override Task OnExecuteInternalAsync(CancellationToken token) 40 | { 41 | var activatedCleaners = _projectCleaners.Where(x => x is not ProjectECodeCleaner || CommandSettings.CleanECode); 42 | foreach (var cleaner in activatedCleaners) 43 | { 44 | AnsiConsole.MarkupLine($"[green]正在清理 {Markup.Escape(cleaner.CleanContent)}[/]"); 45 | if (!CommandSettings.ForceClean && cleaner.Optional && 46 | !AnsiConsole.Confirm($"即将清理 [red]{Markup.Escape(cleaner.CleanContent)}[/],这可能会使您的数据丢失,是否确认清理?", false)) 47 | { 48 | AnsiConsole.MarkupLine("[yellow]您已放弃清理[/]"); 49 | AnsiConsole.WriteLine(); 50 | continue; 51 | } 52 | 53 | foreach (var target in _resolvedConfig.ResolveTargets) 54 | { 55 | try 56 | { 57 | cleaner.Clean(target, _resolvedConfig); 58 | } 59 | catch (Exception) 60 | { 61 | // ignored 62 | } 63 | 64 | if (cleaner.Once) break; 65 | } 66 | 67 | Console.WriteLine(); 68 | } 69 | 70 | return Task.FromResult(0); 71 | } 72 | } -------------------------------------------------------------------------------- /src/EBuild/Program.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Text; 3 | using EBuild.Commands.SubCommands; 4 | using EBuild.DependencyInjection; 5 | using EBuild.Extensions; 6 | using EBuild.Global; 7 | using EBuild.Plugins; 8 | using EBuild.Project; 9 | using EBuild.Project.Cleaners; 10 | using EBuild.Toolchain; 11 | using Microsoft.Extensions.DependencyInjection; 12 | using Microsoft.Extensions.Logging; 13 | using Spectre.Console.Cli; 14 | 15 | Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); 16 | 17 | var services = new Microsoft.Extensions.DependencyInjection.ServiceCollection(); 18 | services.AddLogging(builder => builder.ClearProviders()); 19 | 20 | services.AddSingleton(_ => Defaults.Deserializer); 21 | services.AddSingleton(_ => Defaults.Serializer); 22 | 23 | services.AddSingleton( 24 | x => new EnvironmentVariables(x.GetService>()!) 25 | { 26 | new("EBUILD_EXECUTABLE_PATH", "ebuild可执行文件路径", () => Assembly.GetExecutingAssembly().Location ?? "") 27 | }); 28 | 29 | services.AddImplementation(); 30 | services.AddImplementation(); 31 | services.AddImplementation(); 32 | 33 | services.AddImplementation(); 34 | services.AddImplementation(); 35 | services.AddImplementation(); 36 | 37 | services.AddImplementation(); 38 | 39 | var app = new CommandApp(new TypeRegistrar(services)); 40 | app.Configure(c => 41 | { 42 | #if DEBUG 43 | c.PropagateExceptions(); 44 | c.ValidateExamples(); 45 | #endif 46 | 47 | c.UseStrictParsing(); 48 | c.SetApplicationVersion(Assembly.GetExecutingAssembly().GetName().Version.ToString()); 49 | 50 | c.AddCommand("init") 51 | .WithExample(new[] { "init", "--default" }) 52 | .WithExample(new[] { "init", "--project", "./examples/proj-1" }); 53 | c.AddCommand("info") 54 | .WithExample(new[] { "info", "--project", "./examples/proj-1" }); 55 | c.AddCommand("toolchain"); 56 | c.AddCommand("e2txt") 57 | .WithExample(new[] { "e2txt", "./源码1.e", "我的DLL" }); 58 | c.AddCommand("txt2e") 59 | .WithExample(new[] { "txt2e", "./源码1.e", "我的DLL" }); 60 | c.AddCommand("build") 61 | .WithExample(new[] { "build", "./源码1.e", "我的DLL" }); 62 | c.AddCommand("clean") 63 | .WithExample(new[] { "clean", "--ecode" }) 64 | .WithExample(new[] { "clean", "--ecode", "--force" }); 65 | c.AddCommand("run"); 66 | }); 67 | return app.Run(args); -------------------------------------------------------------------------------- /docs/installation.md: -------------------------------------------------------------------------------- 1 | # 安装 2 | 3 | 在使用`ebuild`之前,您还需要安装以下工具: 4 | 5 | - [易语言](http://www.eyuyan.com/) 6 | - [e2txt](http://e2ee.jimstone.com.cn/) 7 | - [易语言命令行编译工具 ecl](https://bbs.125.la/forum.php?mod=viewthread&tid=14553929&highlight=ecl) 8 | 9 | ## 安装易语言 10 | 11 | 安装易语言使用易语言官方安装包安装完成即可。 12 | 13 | ## 安装`ebuild` 14 | 15 | ### 下载并解压`ebuild` 16 | 17 | 从[GitHub下载ebuild最新版本](https://github.com/SalHe/ebuild/releases),并将其解压到一个文件夹中即可。 18 | 19 | ![解压ebuild](./imgs/unzip_ebuild.png) 20 | 21 | ::: tip 22 | 23 | 为了您的安全,建议您不要从其他地方下载`ebuild`。同时也非常推荐从源文件编译`ebuild`。 24 | 25 | ::: 26 | 27 | ### 配置环境变量 28 | 29 | 可以将`ebuild`安装目录添加到系统环境变量`Path`中去,这样即可在命令行窗口中直接使用`ebuild`。 30 | 31 | ![配置环境变量](./imgs/envir_var.png) 32 | 33 | 您可以将ebuild的安装目录按下图方式追加到`Path`环境变量的末尾去。比如`ebuild`的安装目录为`E:\Users\SalHe\Downloads\ebuild-v1.0.0-windows-386`,则您应在`Path`末尾追加`;E:\Users\SalHe\Downloads\ebuild-v1.0.0-windows-386`(请注意前有分号。) 34 | 35 | ### 创建 `.toolchain` 文件夹 36 | 37 | 在`ebuild`的安装目录下创建名为`.toolchain`的文件夹,用于存放`e2txt`和`ecl`。 38 | 39 | ## 安装`e2txt` 40 | 41 | 从[E2EE](http://e2ee.jimstone.com.cn/downloads/)找到`e2txt 1.2 - 易语言代码文本互转神器`并下载。 42 | 43 | 下载后,将`e2txt`解压到`\.toolchain\e2txt`目录下。 44 | 45 | ![安装e2txt](./imgs/unzip_e2txt.png) 46 | 47 | 48 | ## 安装`ecl` 49 | 50 | 从[易语言命令行编译工具 ecl v1.2.4.3(beta)(出处: 精易论坛)](https://bbs.125.la/forum.php?mod=viewthread&tid=14553929)下载`ecl`,并安装与[安装`e2txt`](#安装e2txt)类似的方式将`ecl`解压到`\.toolchain\ecl`目录下。 51 | 52 | ![安装ecl](./imgs/unzip_ecl.png) 53 | 54 | ## 完成安装后的效果 55 | 56 | 完成安装后,您的目录结构应大致如下: 57 | 58 | ``` 59 | ebuild-v1.0 60 | ├── .toolchain 61 | │ ├── e2txt 62 | │ │ ├── BaseELangIDE.dll 63 | │ │ ├── e2txt-gui.exe 64 | │ │ ├── e2txt.exe 65 | │ │ ├── Lang 66 | │ │ │ └── ELang 67 | │ │ └── readme.txt 68 | │ └── ecl 69 | │ ├── ecl.exe 70 | │ └── 调用例子 71 | │ ├── 普通编译.bat 72 | │ ├── 源码 73 | │ ├── 编译带密码的源码.bat 74 | │ ├── 静态编译.bat 75 | │ └── 黑月编译.bat 76 | └── ebuild.exe 77 | ``` 78 | 79 | ## 检查安装情况 80 | 81 | 打开命令行,执行以下命令,您便可以看到工具链是否安装成功: 82 | 83 | ```shell 84 | ebuild toolchain 85 | ``` 86 | 87 | ![ebuild_toolchain](imgs/ebuild_toolchain.png) 88 | 89 | ## 其他安装方法 90 | 91 | 上述安装步骤可以帮助您在全局都可以使用`ebuild`。但是事实上,您还可以选择其他安装方式。 92 | 以搜索易语言(e.exe)为例,`ebuild`实际上会按照以下顺序搜索工具链(或者您可以[查看代码](https://github.com/SalHe/ebuild/blob/d9afe0e016390e56d4edd639b579a1330193e0a9/toolchain/init.go#L32)以了解): 93 | 94 | - `<工程根目录>\.toolchain\e\e.exe` 95 | - `<当前工作目录>\.toolchain\e\e.exe` 96 | - `\.toolchain\e\e.exe` 97 | - 根据系统查找方式搜索(搜索`Path`变量) 98 | - *查找注册表(仅限于易语言)* 99 | 100 | 所以您也可以将您的工具链安装在以上目录中去。当您需要针对您的工程使用特定版本的其他工具的时候,您就可以将对应的工具放到`<工程根目录>\.toolchain\XXX`中去,这样根据`ebuild`的搜寻顺序,总是能够使用您在工程中指定版本的工具。 -------------------------------------------------------------------------------- /docs/project/run.md: -------------------------------------------------------------------------------- 1 | # 工程脚本 2 | 3 | 也许您在工程需要做除了编译之外的一些重复工作,重复做的话特别烦。一般可以将这些重复工作写成脚本,在需要的时候执行一下脚本即可,就不用手动操作了。`ebuild`刚好集成了一些相关的功能。 4 | 5 | ## 工程相关脚本 6 | 7 | 在`ebuild.yaml`中有一个名为`scripts`的节点,其下可以存放若干脚本,可以使用`ebuild run`执行。您大可将工程相关的一些固定操作写成脚本放到`scripts`中。`ebuild`在执行这些脚本的时候,会传递一些环境变量 8 | 9 | ```yaml 10 | project: 11 | # ...... 12 | scripts: 13 | show-envs: | 14 | @echo off 15 | echo EBuild="%EBUILD_EXECUTABLE_PATH%" 16 | echo 易语言="%ELANG_DIR%" 17 | echo Ecl="%ECL_DIR%" 18 | echo E2Txt="%E2Txt_DIR%" 19 | get-input: | 20 | @echo off 21 | @REM 演示如何获取用户输入 22 | 23 | set /p Username=用户名: 24 | set /p Password=密码: 25 | 26 | echo/ 27 | echo 您的用户名:%Username% 28 | echo 您的密码:%Password% 29 | cmd-args: | 30 | @echo off 31 | echo arg0=%0 32 | echo arg1=%1 33 | echo arg2=%2 34 | echo arg3=%3 35 | ``` 36 | 37 | 在上述配置中我们定义了三个脚本:`show-envs`、`get-input`、`cmd-args`。分别演示了输出`ebuild`传递的环境变量、从标准输入获取用户输入、获取传递给脚本的命令行参数。 38 | 39 | 要执行上述脚本,我们只需要在命令行中执行`ebuild run XXX`。 40 | 41 | ![show-envs](../imgs/config/run_show-envs.png) 42 | 43 | ## 向脚本传递参数 44 | 45 | 如果您要想所执行的脚本传递参数,只需要执行: 46 | 47 | ```shell 48 | ebuild run XXX -- 命令行参数... 49 | ``` 50 | 51 | 如: 52 | 53 | ![cmd-args](../imgs/config/run_cmd-args.png) 54 | 55 | 56 | ## 使用易语言程序作为脚本 57 | 58 | 也许您不熟悉`bat`脚本的编写,那么您可以选择使用易语言来编写您的脚本。然后使用`ebuild`来运行(run)您的源文件即可。 59 | 60 | 比如当前工程有源文件[`<工程根目录>\scripts\易语言做脚本示例.e`](https://github.com/SalHe/ebuild/blob/4f53059ce09148ee58821b8460b8cef8e6bbd18e/examples/simple/scripts/%E6%98%93%E8%AF%AD%E8%A8%80%E5%81%9A%E8%84%9A%E6%9C%AC%E7%A4%BA%E4%BE%8B.e)。其代码如下: 61 | 62 | ``` 63 | .版本 2 64 | 65 | .程序集 程序集1 66 | 67 | .子程序 _启动子程序, 整数型, , 本子程序在程序启动后最先执行 68 | .局部变量 命令行参数, 文本型, , "0" 69 | .局部变量 i, 整数型 70 | 71 | 72 | 标准输出 (, “你好,这是脚本!” + #换行符) 73 | 标准输出 (, “它会被自动编译并执行!” + #换行符) 74 | 标准输出 (, #换行符) 75 | 76 | 标准输出 (, “Ebuild: ” + 读环境变量 (“EBUILD_EXECUTABLE_PATH”) + #换行符) 77 | 标准输出 (, “易语言: ” + 读环境变量 (“ELANG_DIR”) + #换行符) 78 | 标准输出 (, “Ecl: ” + 读环境变量 (“ECL_DIR”) + #换行符) 79 | 标准输出 (, “E2Txt: ” + 读环境变量 (“E2TXT_DIR”) + #换行符) 80 | 标准输出 (, #换行符) 81 | 82 | 标准输出 (, “命令行参数:” + #换行符) 83 | 取命令行 (命令行参数) 84 | .计次循环首 (取数组成员数 (命令行参数), i) 85 | 标准输出 (, 命令行参数 [i] + #换行符) 86 | .计次循环尾 () 87 | 标准输出 (, #换行符) 88 | 89 | 标准输出 (, “工程根路径:” + 读环境变量 (“EBUILD_PROJECT_ROOT_DIR”) + #换行符) 90 | 标准输出 (, “工程输出路径:” + 读环境变量 (“EBUILD_PROJECT_OUTPUT_DIR”) + #换行符) 91 | 标准输出 (, #换行符) 92 | 93 | 返回 (0) ' 可以根据您的需要返回任意数值 94 | ``` 95 | 96 | 97 | 您可以执行以下命令来"执行"易语言源文件: 98 | 99 | ```shell 100 | ebuild run ./scripts/易语言做脚本示例.e -- 传递给易语言程序的命令行参数... 101 | ``` 102 | 103 | 比如: 104 | 105 | !["执行"易语言源文件](../imgs/config/run_e-script.png) 106 | 107 | ::: tip ebuild是如何执行源文件的? 108 | 109 | 其实`ebuild`只是自动使用`ecl`完成了将您所指定的源码编译成可执行文件的过程,在上面的图中我们也可以看到`ebuild`编译源文件的提示。 110 | 111 | 因为"执行"过程中需要编译源文件,所以建议您的源文件不应该太大。 112 | 113 | ::: -------------------------------------------------------------------------------- /src/EBuild/Commands/Base/ProjectCommand.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | using EBuild.Config.Resolved; 3 | using EBuild.Plugins; 4 | using EBuild.Project; 5 | using EBuild.Sources; 6 | using EBuild.Toolchain; 7 | using Spectre.Console; 8 | using Spectre.Console.Cli; 9 | using YamlDotNet.Serialization; 10 | 11 | namespace EBuild.Commands.Base; 12 | 13 | public class ProjectSettings : GeneralSettings 14 | { 15 | [CommandOption("-p|--project")] 16 | [Description("工程根目录,默认为当前工作路径。")] 17 | [DefaultValue(".")] 18 | public string ProjectRoot { get; set; } = Directory.GetCurrentDirectory(); 19 | } 20 | 21 | public class ProjectCommand : CommandBase 22 | where TSettings : ProjectSettings 23 | { 24 | private readonly IDeserializer _deserializer; 25 | protected ResolvedConfig _resolvedConfig; 26 | protected string ProjectRoot { get; private set; } = ""; 27 | 28 | public ProjectCommand(IDeserializer deserializer, IEnumerable plugins) : base(plugins) 29 | { 30 | _deserializer = deserializer; 31 | } 32 | 33 | protected virtual bool ShowLoadConfig() 34 | { 35 | return true; 36 | } 37 | 38 | protected virtual IEnumerable NeededToolchains { get; } = new IToolchain[0]; 39 | 40 | public sealed override async Task OnExecuteAsync(CancellationToken cancellationToken) 41 | { 42 | ProjectRoot = CommandSettings.ProjectRoot; 43 | try 44 | { 45 | ResolveProjectRoot(); 46 | if (!CheckToolchians()) 47 | return 1; 48 | if (ShowLoadConfig()) 49 | { 50 | LoadProject(); 51 | AnsiConsole.MarkupLine("[green]已启用配置:{0}[/]", _resolvedConfig.ConfigFile); 52 | } 53 | } 54 | catch (FileNotFoundException e) 55 | { 56 | Console.Error.WriteLine("找不到对应的配置文件,请检查!"); 57 | return 1; 58 | } 59 | catch (UnauthorizedAccessException e) 60 | { 61 | Console.Error.WriteLine("没有权限访问配置文件。"); 62 | return 1; 63 | } 64 | catch (DirectoryNotFoundException e) 65 | { 66 | Console.Error.WriteLine("找不到对应目录。"); 67 | return 1; 68 | } 69 | 70 | return await OnExecuteInternalAsync(cancellationToken); 71 | } 72 | 73 | private bool CheckToolchians() 74 | { 75 | foreach (var toolchain in NeededToolchains) 76 | { 77 | toolchain.Search(ProjectRoot); 78 | if (!toolchain.Exists()) 79 | { 80 | AnsiConsole.MarkupLine("[red]找不到 {0}[/]", Markup.Escape(toolchain.Description)); 81 | return false; 82 | } 83 | } 84 | 85 | return true; 86 | } 87 | 88 | private void ResolveProjectRoot() 89 | { 90 | ProjectRoot = Path.GetFullPath(ProjectRoot); 91 | } 92 | 93 | protected virtual Task OnExecuteInternalAsync(CancellationToken cancellationToken) 94 | { 95 | return Task.Run(OnExecuteInternal, cancellationToken); 96 | } 97 | 98 | protected virtual int OnExecuteInternal() 99 | { 100 | return 0; 101 | } 102 | 103 | private void LoadProject() 104 | { 105 | var pwdResolver = PasswordFileResolver.FromProjectRootDir(ProjectRoot); 106 | _resolvedConfig = ResolvedConfig.Load(ProjectRoot, _deserializer, pwdResolver); 107 | _resolvedConfig.OutputDir = ProjectPath.GetDefaultOutputPath(ProjectRoot); 108 | } 109 | } -------------------------------------------------------------------------------- /src/EBuild/Commands/SubCommands/InfoCommand.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | using EBuild.Commands.Base; 3 | using EBuild.Config.Resolved; 4 | using EBuild.Plugins; 5 | using EBuild.Yaml.Converters; 6 | using Spectre.Console; 7 | using Spectre.Console.Cli; 8 | using YamlDotNet.Serialization; 9 | 10 | namespace EBuild.Commands.SubCommands; 11 | 12 | [Description("查看当前工程信息。")] 13 | public class InfoCommand : ProjectCommand 14 | { 15 | public class Settings : ProjectSettings 16 | { 17 | [CommandOption("--abs")] 18 | [Description("显示源码的绝对路径")] 19 | public bool AbsolutePath { get; set; } = false; 20 | 21 | [CommandOption("--password")] 22 | [Description("显示源码的密码")] 23 | public bool ShowPassword { get; set; } 24 | } 25 | 26 | 27 | public InfoCommand(IDeserializer deserializer, IEnumerable plugins) : base(deserializer,plugins) 28 | { 29 | } 30 | 31 | protected override int OnExecuteInternal() 32 | { 33 | ShowProjectIntro(); 34 | ShowScripts(); 35 | ShowE2txt(); 36 | ShowResolvedTargets(); 37 | return 0; 38 | } 39 | 40 | private void ShowProjectIntro() 41 | { 42 | AnsiConsole.MarkupLine("[green bold]当前项目信息[/]"); 43 | Console.WriteLine("项目:{0}", _resolvedConfig.RootConfig.Project.Name); 44 | Console.WriteLine("描述:{0}", _resolvedConfig.RootConfig.Project.Description); 45 | Console.WriteLine("版本:{0}", _resolvedConfig.RootConfig.Project.Version); 46 | Console.WriteLine("作者:{0}", _resolvedConfig.RootConfig.Project.Author); 47 | Console.WriteLine("仓库:{0}", _resolvedConfig.RootConfig.Project.Repository); 48 | Console.WriteLine("主页:{0}", _resolvedConfig.RootConfig.Project.Homepage); 49 | Console.WriteLine(); 50 | } 51 | 52 | private void ShowScripts() 53 | { 54 | AnsiConsole.MarkupLine("[green bold]脚本[/]"); 55 | foreach (var (key, value) in _resolvedConfig.RootConfig.Scripts) 56 | Console.WriteLine(key); 57 | Console.WriteLine(); 58 | } 59 | 60 | private void ShowE2txt() 61 | { 62 | AnsiConsole.MarkupLine("[green bold]e2txt配置[/]"); 63 | Console.WriteLine("风格:{0}", 64 | EnumAliasAttribute.GetEnumAliasAttribute(_resolvedConfig.RootConfig.E2Txt.NameStyle)!.Name); 65 | Console.WriteLine("生成易代码:{0}", _resolvedConfig.RootConfig.E2Txt.GenerateE); 66 | Console.WriteLine(); 67 | } 68 | 69 | private void ShowResolvedTargets() 70 | { 71 | AnsiConsole.MarkupLine("[green bold]构建目标 [grey]{0}[/][/]", _resolvedConfig.OutputDir); 72 | var table = new Table(); 73 | table.AddColumn("源码"); 74 | table.AddColumn("来源"); 75 | if (CommandSettings.ShowPassword) 76 | table.AddColumn("密码"); 77 | table.AddColumn("构建"); 78 | table.AddColumn("编译器"); 79 | table.AddColumn("输出"); 80 | foreach (var target in _resolvedConfig.ResolveTargets) 81 | { 82 | var cols = new List(); 83 | cols.Add(Markup.Escape(CommandSettings.AbsolutePath 84 | ? target.Target.Source 85 | : Path.GetRelativePath(ProjectRoot, target.Target.Source))); 86 | switch (target.Origin) 87 | { 88 | case TargetOrigin.Custom: 89 | cols.Add("自定义"); 90 | break; 91 | case TargetOrigin.Search: 92 | cols.Add("搜索"); 93 | break; 94 | } 95 | 96 | if (CommandSettings.ShowPassword) 97 | cols.Add(Markup.Escape(target.Password)); 98 | 99 | cols.Add(target.ShouldBuild ? "[green]:check_mark:[/]" : "[red]:cross_mark:[/]"); 100 | cols.Add(Markup.Escape(EnumAliasAttribute.GetEnumAliasAttribute(target.Target.Build.Compiler)?.Name ?? 101 | target.Target.Build.Compiler.ToString())); 102 | cols.Add(Markup.Escape(target.Target.Output)); 103 | 104 | table.AddRow(cols.ToArray()); 105 | } 106 | 107 | AnsiConsole.Write(table); 108 | } 109 | } -------------------------------------------------------------------------------- /src/EBuild/Commands/Base/TargetCommand.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | using EBuild.Config.Resolved; 3 | using EBuild.Consoles; 4 | using EBuild.Plugins; 5 | using EBuild.Yaml.Converters; 6 | using Spectre.Console; 7 | using Spectre.Console.Cli; 8 | using YamlDotNet.Serialization; 9 | 10 | namespace EBuild.Commands.Base; 11 | 12 | public class TargetSettings : ProjectSettings 13 | { 14 | [CommandOption("-c|--concurrency")] 15 | [Description("并行任务个数。")] 16 | public int ConcurrencyCount { get; init; } = 1; 17 | 18 | [CommandArgument(0, "[目标或源文件...]")] 19 | [Description("参与本次操作的目标或源文件。")] 20 | public string[] Targets { get; init; } = Array.Empty(); 21 | } 22 | 23 | public abstract class TargetCommand : ProjectCommand 24 | where TSettings : TargetSettings 25 | { 26 | protected enum WholeStatus 27 | { 28 | Doing, 29 | Completed, 30 | ErrorOccured 31 | } 32 | 33 | protected virtual int MaxConcurrencyCount => int.MaxValue; 34 | 35 | public TargetCommand(IDeserializer deserializer, IEnumerable plugins) : base(deserializer, plugins) 36 | { 37 | } 38 | 39 | protected abstract string GenerateHeading(WholeStatus status); 40 | 41 | protected virtual IMultiTaskDisplayer GetDisplayer(IList targets) 42 | { 43 | return new TableDisplayer(targets) 44 | { 45 | TargetName = target => target.Target.DisplayName(ProjectRoot), 46 | StatusString = e => EnumAliasAttribute.GetEnumAliasAttribute(e).Name 47 | }; 48 | } 49 | 50 | protected sealed override async Task OnExecuteInternalAsync(CancellationToken cancellationToken) 51 | { 52 | var possibleFile = CommandSettings.Targets.Select(x => Path.GetFullPath(x, ProjectRoot)); 53 | 54 | var activatedTargets = _resolvedConfig.ResolveTargets 55 | .Where(x => CommandSettings.Targets.Length <= 0 || CommandSettings.Targets.Contains(x.Target.Name) || 56 | possibleFile.Contains(x.Target.Source)) 57 | .ToList(); 58 | 59 | if (activatedTargets.Count <= 0) 60 | { 61 | AnsiConsole.MarkupLine("[bold]没有找到符合要求的目标哦。[/]"); 62 | return 0; 63 | } 64 | 65 | var displayer = GetDisplayer(activatedTargets); 66 | 67 | return await displayer.StartAsync(_ => 68 | Task.FromResult(StartTargetTasks(activatedTargets, displayer, cancellationToken))); 69 | } 70 | 71 | private int StartTargetTasks(IList activatedTargets, 72 | IMultiTaskDisplayer displayer, 73 | CancellationToken cancellationToken) 74 | { 75 | var c = Math.Min(CommandSettings.ConcurrencyCount, MaxConcurrencyCount); 76 | var semaphore = new Semaphore(c, c); 77 | var allOk = true; 78 | 79 | displayer.SetHeader(GenerateHeading(WholeStatus.Doing)); 80 | 81 | var tasks = new List(); 82 | for (int i = 0; i < activatedTargets.Count(); i++) 83 | { 84 | var activatedTarget = activatedTargets[i]; 85 | 86 | var task = Task.Run(async () => 87 | { 88 | var updateTargetStatus = displayer.GetUpdater(activatedTarget); 89 | updateTargetStatus(TargetStatus.Waiting, "正在等待..."); 90 | 91 | if (await OnPreDoTargetAsync(activatedTarget, updateTargetStatus, cancellationToken)) 92 | { 93 | semaphore.WaitOne(); 94 | if (!await OnDoTargetAsync(activatedTarget, updateTargetStatus, cancellationToken)) 95 | allOk = false; 96 | semaphore.Release(); 97 | } 98 | }, cancellationToken); 99 | tasks.Add(task); 100 | } 101 | 102 | Task.WaitAll(tasks.ToArray(), cancellationToken); 103 | 104 | displayer.SetHeader(GenerateHeading(allOk ? WholeStatus.Completed : WholeStatus.ErrorOccured)); 105 | return allOk ? 0 : 1; 106 | } 107 | 108 | protected abstract Task OnPreDoTargetAsync(ResolvedTarget target, 109 | Action updateTargetStatus, 110 | CancellationToken cancellationToken); 111 | 112 | protected abstract Task OnDoTargetAsync(ResolvedTarget target, 113 | Action updateTargetStatus, 114 | CancellationToken cancellationToken); 115 | } 116 | 117 | public enum TargetStatus 118 | { 119 | [EnumAlias("等待中")] Waiting, 120 | 121 | [EnumAlias("[yellow]:red_exclamation_mark:[/]")] 122 | Skipped, 123 | [EnumAlias("进行中")] Doing, 124 | [EnumAlias("[green]:check_mark:[/]")] Done, 125 | [EnumAlias("[red]:cross_mark:[/]")] Error 126 | } -------------------------------------------------------------------------------- /src/EBuild/Config/Resolved/ResolvedConfig.cs: -------------------------------------------------------------------------------- 1 | using EBuild.Extensions; 2 | using EBuild.Project; 3 | using EBuild.Sources; 4 | using Microsoft.Extensions.FileSystemGlobbing; 5 | using Microsoft.Extensions.FileSystemGlobbing.Abstractions; 6 | using Spectre.Console; 7 | using YamlDotNet.Serialization; 8 | 9 | namespace EBuild.Config.Resolved; 10 | 11 | public class ResolvedConfig 12 | { 13 | public string ProjectRootDir { get; private init; } 14 | public string OutputDir { get; set; } 15 | public string ConfigFile => ProjectPath.GetConfigFilePath(ProjectRootDir); 16 | public RootConfig RootConfig { get; private set; } 17 | public IReadOnlyList ResolveTargets { get; private set; } 18 | 19 | private ResolvedConfig() 20 | { 21 | } 22 | 23 | public static ResolvedConfig Load(string projectRoot, IDeserializer deserializer, 24 | PasswordFileResolver passwordFileResolver) 25 | { 26 | var resolvedConfig = new ResolvedConfig 27 | { 28 | ProjectRootDir = projectRoot 29 | }; 30 | resolvedConfig.RootConfig = deserializer.Deserialize(File.OpenText(resolvedConfig.ConfigFile)); 31 | var resolvedTargets = new List(); 32 | resolvedConfig.ResolveTargets = resolvedTargets; 33 | 34 | // 自定义的源码 35 | var sourcesAdded = new HashSet(); 36 | if (resolvedConfig.RootConfig.Targets != null) 37 | { 38 | foreach (var targetInConfig in resolvedConfig.RootConfig.Targets) 39 | { 40 | targetInConfig.Source = Path.GetFullPath(targetInConfig.Source, projectRoot); 41 | if (targetInConfig.Build == null) 42 | targetInConfig.Build = resolvedConfig.RootConfig.Build; 43 | 44 | sourcesAdded.Add(targetInConfig.Source); 45 | var pwd = passwordFileResolver.Resolve(targetInConfig.Source); 46 | resolvedTargets.Add(new ResolvedTarget(targetInConfig, TargetOrigin.Custom, Password: pwd)); 47 | } 48 | } 49 | 50 | // 搜索的源码 51 | var matcher = new Matcher(); 52 | matcher.AddIncludePatterns(resolvedConfig.RootConfig.Includes); 53 | matcher.AddExcludePatterns(resolvedConfig.RootConfig.Excludes); 54 | var result = matcher.Execute(new DirectoryInfoWrapper(new DirectoryInfo(projectRoot))); 55 | var excludeBuildsMatcher = new Matcher(); 56 | excludeBuildsMatcher.AddIncludePatterns(resolvedConfig.RootConfig.ExcludeBuilds); 57 | foreach (var file in result.Files) 58 | { 59 | var sourcePath = Path.GetFullPath(file.Path, projectRoot); 60 | if (sourcesAdded.Contains(sourcePath)) 61 | continue; 62 | var pwd = passwordFileResolver.Resolve(sourcePath); 63 | 64 | var target = new Target() 65 | { 66 | Name = string.Empty, 67 | Source = sourcePath, 68 | Output = Path.ChangeExtension(Path.GetRelativePath(projectRoot, sourcePath), 69 | null), 70 | Package = false, 71 | Build = resolvedConfig.RootConfig.Build 72 | }; 73 | 74 | resolvedTargets.Add(new ResolvedTarget(target, TargetOrigin.Search, 75 | !excludeBuildsMatcher.Match(file.Path).HasMatches, pwd)); 76 | } 77 | 78 | foreach (var target in resolvedConfig.ResolveTargets) 79 | { 80 | if (string.IsNullOrEmpty(target.Password) && !Path.HasExtension(target.Target.Output) && 81 | target.SourceMeta != null) 82 | { 83 | if (target.Origin == TargetOrigin.Custom) 84 | { 85 | AnsiConsole.MarkupLine( 86 | $"[yellow]您可能未给{Markup.Escape("[")}{{0}}{Markup.Escape("]")}指定输出后缀,易语言会自动加上后缀,请注意。[/]", 87 | Markup.Escape(target.Target.Name)); 88 | } 89 | else 90 | { 91 | Attributes.GetEnumValueAttribute(target.SourceMeta.TargetType, 92 | out ESourceTargetTypeAttribute? attribute); 93 | target.Target.Output = Path.ChangeExtension(target.Target.Output, attribute!.Extension); 94 | } 95 | } 96 | 97 | if (target.SourceMeta == null) 98 | AnsiConsole.MarkupLine("[yellow]{0} 可能不是一个合法的源文件。[/]", Markup.Escape(target.Target.Source)); 99 | } 100 | 101 | return resolvedConfig; 102 | } 103 | } 104 | 105 | public enum TargetOrigin 106 | { 107 | Search, 108 | Custom 109 | } 110 | 111 | public class ResolvedTarget 112 | { 113 | public ResolvedTarget(Target Target, 114 | TargetOrigin Origin = TargetOrigin.Search, 115 | bool ShouldBuild = true, 116 | string Password = "") 117 | { 118 | this.Target = Target; 119 | this.Origin = Origin; 120 | this.ShouldBuild = ShouldBuild; 121 | this.Password = Password; 122 | this.SourceMeta = ESourceMeta.FromSource(Target.Source); 123 | } 124 | 125 | public Target Target { get; init; } 126 | public TargetOrigin Origin { get; init; } 127 | public bool ShouldBuild { get; init; } 128 | public string Password { get; init; } 129 | public ESourceMeta? SourceMeta { get; } 130 | } -------------------------------------------------------------------------------- /src/EBuild/Commands/SubCommands/E2TxtCommand.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | using System.Diagnostics; 3 | using System.Text; 4 | using System.Text.RegularExpressions; 5 | using EBuild.Commands.Base; 6 | using EBuild.Config.Resolved; 7 | using EBuild.Plugins; 8 | using EBuild.Project; 9 | using EBuild.Toolchain; 10 | using Spectre.Console; 11 | using YamlDotNet.Serialization; 12 | 13 | namespace EBuild.Commands.SubCommands; 14 | 15 | [Description("将易语言代码转换到txt。")] 16 | public class E2TxtCommand : TargetCommand 17 | { 18 | private readonly E2TxtToolchain _e2txt; 19 | 20 | public E2TxtCommand(IDeserializer deserializer, E2TxtToolchain e2txt, IEnumerable plugins) : base( 21 | deserializer, plugins) 22 | { 23 | _e2txt = e2txt; 24 | } 25 | 26 | protected override IEnumerable NeededToolchains => new IToolchain[] { _e2txt }; 27 | 28 | protected override string GenerateHeading(WholeStatus status) 29 | { 30 | switch (status) 31 | { 32 | case WholeStatus.Doing: 33 | return "e2txt"; 34 | case WholeStatus.Completed: 35 | return "[green]:check_mark:e2txt[/]"; 36 | case WholeStatus.ErrorOccured: 37 | return "[red]:cross_mark:e2txt[/]"; 38 | } 39 | 40 | return ""; 41 | } 42 | 43 | private static readonly Regex _multispaces = new Regex(@"\s+"); 44 | 45 | protected override async Task OnDoTargetAsync(ResolvedTarget target, 46 | Action updateTargetStatus, 47 | CancellationToken cancellationToken) 48 | { 49 | updateTargetStatus(TargetStatus.Doing, "开始转换"); 50 | 51 | var noError = true; 52 | var convertOk = false; 53 | 54 | var args = GetArgs(target); 55 | updateTargetStatus(TargetStatus.Doing, 56 | string.Format("[grey]{0}[/]", Markup.Escape(_e2txt.ExecutablePath + " " + string.Join(" ", args)))); 57 | 58 | var process = new Process(); 59 | foreach (var s in args) process.StartInfo.ArgumentList.Add(s); 60 | process.StartInfo.FileName = _e2txt.ExecutablePath; 61 | process.StartInfo.RedirectStandardOutput = true; 62 | process.StartInfo.RedirectStandardError = true; 63 | process.StartInfo.StandardOutputEncoding = Encoding.GetEncoding("gbk"); 64 | process.StartInfo.StandardErrorEncoding = Encoding.GetEncoding("gbk"); 65 | 66 | var error = ""; 67 | 68 | var d = (DataReceivedEventHandler)((sender, eventArgs) => 69 | { 70 | if (!string.IsNullOrEmpty(eventArgs.Data)) 71 | { 72 | if (eventArgs.Data.StartsWith("SUCC:")) 73 | { 74 | // 成功 75 | convertOk = true; 76 | var outputDir = eventArgs.Data.Substring(5); 77 | updateTargetStatus(TargetStatus.Done, 78 | string.Format("[green]转换成功:{0}[/]", 79 | Markup.Escape(outputDir) 80 | ) 81 | ); 82 | } 83 | else if (eventArgs.Data.StartsWith("ERROR:")) 84 | { 85 | // 错误提示 86 | noError = false; 87 | error += eventArgs.Data.Substring(6) + "\n"; 88 | updateTargetStatus(TargetStatus.Error, 89 | string.Format("[red]转换出错:{0}[/]", 90 | Markup.Escape(_multispaces.Replace(error, " ")) 91 | ) 92 | ); 93 | } 94 | else 95 | { 96 | // 日志 97 | if (!convertOk) 98 | updateTargetStatus(noError ? TargetStatus.Doing : TargetStatus.Error, 99 | Markup.Escape(_multispaces.Replace(eventArgs.Data, " "))); 100 | } 101 | } 102 | }); 103 | process.OutputDataReceived += d; 104 | process.ErrorDataReceived += d; 105 | 106 | process.Start(); 107 | process.BeginOutputReadLine(); 108 | process.BeginErrorReadLine(); 109 | cancellationToken.Register(() => 110 | { 111 | process.Kill(); 112 | process.CancelOutputRead(); 113 | process.CancelErrorRead(); 114 | }); 115 | await process.WaitForExitAsync(cancellationToken); 116 | 117 | if (!noError) 118 | { 119 | updateTargetStatus(TargetStatus.Error, 120 | string.Format("[red]转换过程中出现错误:\n{0}[/]", 121 | Markup.Escape(error) 122 | ) 123 | ); 124 | } 125 | 126 | return convertOk; 127 | } 128 | 129 | protected virtual IList GetArgs(ResolvedTarget target) 130 | { 131 | return E2TxtToolchain.E2TxtArgs(target.Target.Source, target.Target.GetECodeDir(), 132 | _resolvedConfig.RootConfig.E2Txt); 133 | } 134 | 135 | protected override Task OnPreDoTargetAsync(ResolvedTarget target, 136 | Action updateTargetStatus, 137 | CancellationToken cancellationToken) 138 | { 139 | if (string.IsNullOrEmpty(target.Password)) 140 | { 141 | updateTargetStatus(TargetStatus.Waiting, "等待转换中..."); 142 | return Task.FromResult(true); 143 | } 144 | 145 | updateTargetStatus(TargetStatus.Skipped, "[yellow]源文件存在密码,已跳过转换![/]"); 146 | return Task.FromResult(false); 147 | } 148 | } -------------------------------------------------------------------------------- /src/EBuild/Toolchain/EclToolchain.cs: -------------------------------------------------------------------------------- 1 | using System.Text.RegularExpressions; 2 | using EBuild.Config; 3 | using EBuild.Config.Resolved; 4 | using EBuild.Sources; 5 | using EBuild.Yaml.Converters; 6 | 7 | namespace EBuild.Toolchain; 8 | 9 | public class EclToolchain : GeneralToolchain 10 | { 11 | private enum Error 12 | { 13 | [EnumAlias("编译成功")] Ok = 0, 14 | [EnumAlias("处理成功")] Success = 1, 15 | 16 | [EnumAlias("未定义类型的错误")] Unknown = -1, 17 | [EnumAlias("命令行有错误")] Param = -2, 18 | [EnumAlias("找不到文件")] FileNotFound = -3, 19 | [EnumAlias("文件无效")] FileInvalid = -4, 20 | [EnumAlias("编译失败")] Compile = -5, 21 | [EnumAlias("不支持的编译类型")] InvalidCompileType = -6, 22 | [EnumAlias("无法识别或无法运行的易语言程序")] ECannotStart = -7, 23 | [EnumAlias("无法获取易语言菜单")] CanNotGetMenu = -8, 24 | [EnumAlias("易语言意外结束")] Shutdown = -9, 25 | [EnumAlias("静态编译失败")] Static = -10, 26 | [EnumAlias("生成link.ini文件过程中出错")] MakeLinkIni = -11, 27 | [EnumAlias("老版黑月的相关数据无法定位")] BmInfo = -12, 28 | [EnumAlias("黑月编译失败")] BmCompile = -13, 29 | [EnumAlias("源码密码不正确")] Password = -14, 30 | [EnumAlias("缺乏易模块")] EC = -15, 31 | [EnumAlias("缺少支持库")] ELib = -16, 32 | [EnumAlias("启动易语言超时")] StartTimeout = -17, 33 | [EnumAlias("编译超时")] CompileTimeout = -18, 34 | [EnumAlias("不支持易包编译")] NotSupportEPkg = -19, 35 | } 36 | 37 | public override IList EnvironmentVariables => new List() 38 | { 39 | new("ECL_DIR", "ecl安装路径", () => Path.GetDirectoryName(ExecutablePath) ?? ""), 40 | }; 41 | 42 | public EclToolchain() : base("易语言命令行编译工具", "https://bbs.125.la/forum.php?mod=viewthread&tid=14553929&highlight=ecl", 43 | "ecl") 44 | { 45 | } 46 | 47 | public static IList Args(ResolvedTarget target, string outputDir, bool hideSecret = false) 48 | { 49 | return Args( 50 | target.Target.Source, 51 | target.Target.OutputPath(outputDir), 52 | target.SourceMeta, 53 | target.Target.Build?.Compiler, 54 | target.Target.CompileConfig, 55 | target.Target.CompileDescription, 56 | target.Password, 57 | target.Target.Package, 58 | hideSecret 59 | ); 60 | } 61 | 62 | public static IList Args(string source, string outputPath, ESourceMeta? sourceMeta, Compiler? compiler, 63 | string compileConfig, string compileDescription, string password, bool isPackage, 64 | bool hideSecret = false) 65 | { 66 | var args = new List() { "make", source, outputPath }; 67 | if (isPackage) args.Add("-p"); 68 | else 69 | { 70 | if (sourceMeta?.TargetType != TargetType.LinuxEc && 71 | sourceMeta?.TargetType != TargetType.WinEc) // 编译模块不用选择编译器 72 | args.AddRange(CompilerArgs(compiler, compileConfig, 73 | compileDescription)); 74 | if (!string.IsNullOrEmpty(password)) 75 | args.AddRange(new[] { "-pwd", hideSecret ? "******" : password }); 76 | } 77 | 78 | return args; 79 | } 80 | 81 | public static IList CompilerArgs(Compiler? compiler, string bmConfig, string bmDescription) 82 | { 83 | var args = new List(); 84 | if (compiler != null) 85 | { 86 | switch (compiler) 87 | { 88 | case Compiler.Static: 89 | args.Add("-s"); 90 | break; 91 | case Compiler.Normal: 92 | break; 93 | case Compiler.Independent: 94 | args.Add("-d"); 95 | break; 96 | case Compiler.BlackMoon: 97 | args.Add("-bm"); 98 | break; 99 | case Compiler.BlackMoonAsm: 100 | args.Add("-bm0"); 101 | break; 102 | case Compiler.BlackMoonCpp: 103 | args.Add("-bm1"); 104 | break; 105 | case Compiler.BlackMoonMFC: 106 | args.Add("-bm2"); 107 | break; 108 | default: 109 | throw new ArgumentOutOfRangeException(nameof(compiler), compiler, null); 110 | } 111 | 112 | if (compiler.ToString()!.StartsWith("BlackMoon")) 113 | { 114 | if (!string.IsNullOrEmpty(bmConfig)) args.AddRange(new string[] { "-bmcfg", bmConfig }); 115 | if (!string.IsNullOrEmpty(bmDescription)) args.AddRange(new string[] { "-bmdesc", bmDescription }); 116 | } 117 | } 118 | 119 | return args; 120 | } 121 | 122 | private static readonly Regex errorMatcher = new Regex(@"\(错误:(-\d+)\)(.+)"); 123 | 124 | public static bool TryMatchError(string log, out bool compileOk, out int errorCode, out string tip) 125 | { 126 | var match = errorMatcher.Match(log); 127 | if (!match.Success) 128 | { 129 | errorCode = 0; 130 | tip = ""; 131 | compileOk = false; 132 | return false; 133 | } 134 | 135 | errorCode = int.Parse(match.Groups[1].Value); 136 | var error = (Error)errorCode; 137 | // tip = EnumAliasAttribute.GetEnumAliasAttribute(error)?.Name ?? "ebuild暂未记录该错误代码"; 138 | tip = match.Groups[2].Value; 139 | compileOk = error is Error.Ok or Error.Success; 140 | return true; 141 | } 142 | } -------------------------------------------------------------------------------- /src/EBuild/Commands/SubCommands/RunCommand.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | using System.ComponentModel.DataAnnotations; 3 | using System.Diagnostics; 4 | using EBuild.Commands.Base; 5 | using EBuild.Config; 6 | using EBuild.Plugins; 7 | using EBuild.Project; 8 | using EBuild.Sources; 9 | using EBuild.Toolchain; 10 | using Spectre.Console; 11 | using Spectre.Console.Cli; 12 | using YamlDotNet.Serialization; 13 | 14 | namespace EBuild.Commands.SubCommands; 15 | 16 | [Description(@"运行脚本或易语言源文件。 17 | 18 | 当第一个参数对应了配置文件中某一脚本名字时,将执行脚本对应的内容; 19 | 当第一个参数对应了某一个源码的文件名时,将编译该易语言源文件并执行(要求源文件必须是Windows可执行程序或Windows控制台程序,由于需要编译,建议源文件尽可能小)。 20 | 21 | 由于执行的对象可能是脚本或者易语言源文件,所以建议您为脚本命名的时候不要与源文件重名。 22 | 此外,目前ebuild不会保证到底先执行哪种情况,所以更加建议您不要重名。 23 | 24 | 当您执行易语言源文件时,请您自行确保源文件的安全性。")] 25 | public class RunCommand : ProjectCommand 26 | { 27 | public class Settings : ProjectSettings 28 | { 29 | [CommandArgument(0, "<脚本名称|易语言源文件>")] 30 | [Required] 31 | public string TargetToRun { get; init; } 32 | 33 | [CommandOption("--verbose")] 34 | [Description("输出编译源文件的输出日志。")] 35 | [DefaultValue(false)] 36 | public bool Verbose { get; init; } 37 | } 38 | 39 | private readonly EclToolchain _eclToolchain; 40 | private readonly EnvironmentVariables _environmentVariables; 41 | protected override IEnumerable NeededToolchains { get; } 42 | 43 | public RunCommand(IDeserializer deserializer, IEnumerable toolchains, EclToolchain eclToolchain, 44 | EnvironmentVariables environmentVariables, IEnumerable plugins) : 45 | base(deserializer, plugins) 46 | { 47 | NeededToolchains = toolchains; 48 | _eclToolchain = eclToolchain; 49 | _environmentVariables = environmentVariables; 50 | } 51 | 52 | protected override async Task OnExecuteInternalAsync(CancellationToken cancellationToken) 53 | { 54 | var fullPath = Path.GetFullPath(CommandSettings.TargetToRun); 55 | var exeOrBatPath = ""; 56 | if (File.Exists(fullPath)) 57 | { 58 | AnsiConsole.MarkupLine("[yellow]源文件`{0}`存在,将尝试编译该文件并运行。[/]", fullPath); 59 | var meta = ESourceMeta.FromSource(fullPath); 60 | if (meta == null || meta.SourceType == SourceType.ECom || 61 | (meta.TargetType != TargetType.WinConsole && meta.TargetType != TargetType.WinForm)) 62 | { 63 | AnsiConsole.MarkupLine("[red]您必须提供一个有效的易语言源文件,并且该源码应编译成Windows控制台程序或Windows窗口程序。[/]"); 64 | return 1; 65 | } 66 | 67 | exeOrBatPath = await QuickBuildESource( 68 | cancellationToken, _eclToolchain, 69 | CommandSettings.Verbose, 70 | fullPath, meta); 71 | if (!string.IsNullOrEmpty(exeOrBatPath)) 72 | AnsiConsole.MarkupLine("[yellow]编译成功,准备运行:`{0}`[/]", Markup.Escape(exeOrBatPath)); 73 | } 74 | else if (_resolvedConfig.RootConfig.Scripts?.ContainsKey(CommandSettings.TargetToRun) == true) 75 | { 76 | var tempFile = Path.GetTempFileName(); 77 | var batContent = _resolvedConfig.RootConfig.Scripts[CommandSettings.TargetToRun]; 78 | batContent = batContent.ReplaceLineEndings("\r\n"); 79 | await File.WriteAllTextAsync(tempFile, batContent, cancellationToken); 80 | exeOrBatPath = Path.ChangeExtension(tempFile, "ebuild-run.bat"); 81 | File.Move(tempFile, exeOrBatPath); 82 | 83 | AnsiConsole.MarkupLine("[yellow]准备运行:`{0}`[/]", Markup.Escape(exeOrBatPath)); 84 | } 85 | else 86 | { 87 | AnsiConsole.MarkupLine("[red]找不到对于的脚本({0})或源码({1})[/]", Markup.Escape(fullPath), 88 | Markup.Escape(CommandSettings.TargetToRun)); 89 | return 1; 90 | } 91 | 92 | var process = new Process(); 93 | foreach (var arg in CommandContext.Remaining.Raw) process.StartInfo.ArgumentList.Add(arg); 94 | process.StartInfo.FileName = exeOrBatPath; 95 | 96 | _environmentVariables.ForProject(ProjectRoot, _resolvedConfig.OutputDir); 97 | _environmentVariables.LoadToStringDictionary(process.StartInfo.EnvironmentVariables); 98 | 99 | process.Start(); 100 | 101 | cancellationToken.Register(() => 102 | { 103 | process.Kill(); 104 | File.Delete(exeOrBatPath); 105 | }); 106 | await process.WaitForExitAsync(cancellationToken); 107 | 108 | process.Kill(); 109 | 110 | File.Delete(exeOrBatPath); 111 | return process.ExitCode; 112 | } 113 | 114 | private static async Task QuickBuildESource(CancellationToken cancellationToken, EclToolchain eclToolchain, 115 | bool verbose, string fullPath, ESourceMeta meta) 116 | { 117 | var tempFile = Path.GetTempFileName(); 118 | var tempExe = Path.ChangeExtension(tempFile, "ebuild-run.exe"); 119 | File.Delete(tempFile); 120 | 121 | var process = new Process(); 122 | foreach (var arg in EclToolchain.Args(fullPath, tempExe, meta, Compiler.Normal, "", "", "", false)) 123 | process.StartInfo.ArgumentList.Add(arg); 124 | process.StartInfo.FileName = eclToolchain.ExecutablePath; 125 | if (!verbose) 126 | { 127 | process.StartInfo.RedirectStandardError = true; 128 | process.StartInfo.RedirectStandardOutput = true; 129 | } 130 | 131 | cancellationToken.Register(() => 132 | { 133 | process.Kill(); 134 | File.Delete(tempExe); 135 | tempExe = ""; 136 | }); 137 | process.Start(); 138 | await process.WaitForExitAsync(cancellationToken); 139 | return tempExe; 140 | } 141 | } -------------------------------------------------------------------------------- /src/EBuild/Commands/SubCommands/InitCommand.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | using EBuild.Commands.Base; 3 | using EBuild.Config; 4 | using EBuild.Plugins; 5 | using EBuild.Project; 6 | using EBuild.Yaml.Converters; 7 | using Spectre.Console; 8 | using Spectre.Console.Cli; 9 | using YamlDotNet.Serialization; 10 | 11 | namespace EBuild.Commands.SubCommands; 12 | 13 | [Description("初始化工程。")] 14 | public class InitCommand : ProjectCommand 15 | { 16 | public class Settings : ProjectSettings 17 | { 18 | [CommandOption("-d|--default")] 19 | [Description("采用默认配置初始化工程。")] 20 | public bool DefaultInit { get; init; } 21 | } 22 | 23 | private readonly ISerializer _serializer; 24 | 25 | public InitCommand(IDeserializer deserializer, ISerializer serializer, IEnumerable plugins) : base(deserializer,plugins) 26 | { 27 | _serializer = serializer; 28 | } 29 | 30 | protected override bool ShowLoadConfig() 31 | { 32 | return false; 33 | } 34 | 35 | protected override int OnExecuteInternal() 36 | { 37 | var configFilePath = ProjectPath.GetConfigFilePath(ProjectRoot); 38 | if (Directory.Exists(ProjectRoot) && Directory.GetFiles(ProjectRoot).Length > 0) 39 | { 40 | AnsiConsole.MarkupLine("[red]目录不为空,不能创建ebuild工程:{0}[/]", configFilePath); 41 | return 1; 42 | } 43 | 44 | var rootConfig = CreateRootConfig(); 45 | Directory.CreateDirectory(ProjectRoot); 46 | try 47 | { 48 | File.WriteAllText(configFilePath, _serializer.Serialize(rootConfig)); 49 | File.WriteAllText(Path.GetFullPath(".gitignore", ProjectRoot), @" 50 | # 恢复出来的易语言源文件和密码文件不纳入版本控制 51 | *.recover.e 52 | ebuild.pwd.yaml 53 | **/*.ecode/log 54 | 55 | # 易语言产生的备份源码文件 56 | *.bak 57 | 58 | # 编译输出 59 | ebuild-out/"); 60 | File.WriteAllText(ProjectPath.GetSourcePasswordFilePath(ProjectRoot), "./带密码的源码.e: 123456"); 61 | File.WriteAllText(Path.GetFullPath("README.md", ProjectRoot), @$"# {rootConfig.Project.Name} 62 | 63 | ## 编译 64 | 65 | ```shell 66 | ebuild build 67 | ``` 68 | 69 | ## e2txt 70 | 71 | ```shell 72 | ebuild e2txt 73 | ``` 74 | "); 75 | } 76 | catch (Exception e) 77 | { 78 | AnsiConsole.MarkupLine("[red]创建工程失败!{0}[/]", e); 79 | return 1; 80 | } 81 | 82 | AnsiConsole.WriteLine(); 83 | AnsiConsole.MarkupLine("[green]创建工程完成。[/]"); 84 | AnsiConsole.MarkupLine("[green]有关工程的配置可以参见ebuild示例代码:https://github.com/SalHe/ebuild/tree/master/examples[/]"); 85 | AnsiConsole.MarkupLine("[green]此外,您也可以去ebuild官方网站查看更加详细的文档:https://salhe.github.io/ebuild/[/]"); 86 | return 0; 87 | } 88 | 89 | private RootConfig CreateRootConfig() 90 | { 91 | var defaultProjectName = Path.GetFileName(ProjectRoot); 92 | var project = new Config.Project 93 | { 94 | Name = defaultProjectName, 95 | Version = "1.0", 96 | Description = $"{Environment.UserName}的工程:{defaultProjectName}", 97 | Author = Environment.UserName, 98 | Repository = $"https://github.com/{Environment.UserName}/{defaultProjectName}", 99 | Homepage = $"https://github.com/{Environment.UserName}" 100 | }; 101 | var rootConfig = new RootConfig 102 | { 103 | Project = project, 104 | Scripts = new Dictionary 105 | { 106 | { 107 | "hello", "@echo off\necho Hello ebuild!" 108 | } 109 | }, 110 | Includes = new List 111 | { 112 | "**/*.e" 113 | }, 114 | Excludes = new List 115 | { 116 | "**/*.recover.e", 117 | "**/*.ecode/**.e", 118 | "**/*.代码/**.e" 119 | }, 120 | ExcludeBuilds = new List 121 | { 122 | "./scripts/**/*.e" 123 | }, 124 | Build = new Build 125 | { 126 | Compiler = Compiler.Static 127 | }, 128 | E2Txt = new Config.E2Txt 129 | { 130 | GenerateE = true, 131 | NameStyle = Config.E2Txt.NameStyleEnum.Chinese 132 | } 133 | }; 134 | if (!CommandSettings.DefaultInit) 135 | { 136 | project.Name = AnsiConsole.Ask("工程", project.Name); 137 | project.Version = AnsiConsole.Ask("版本", project.Version); 138 | project.Description = AnsiConsole.Ask("说明", project.Description); 139 | project.Author = AnsiConsole.Ask("作者", project.Author); 140 | project.Repository = AnsiConsole.Ask("仓库", project.Repository); 141 | project.Homepage = AnsiConsole.Ask("主页", project.Homepage); 142 | 143 | rootConfig.E2Txt.GenerateE = AnsiConsole.Confirm(Markup.Escape("[e2txt]生成易代码"), rootConfig.E2Txt.GenerateE); 144 | rootConfig.E2Txt.NameStyle = AnsiConsole.Prompt( 145 | new SelectionPrompt() 146 | .Title(Markup.Escape("[e2txt]命名风格")) 147 | .UseConverter(DisplaySelector) 148 | .AddChoices(Enum.GetValues()) 149 | ); 150 | AnsiConsole.MarkupLine(Markup.Escape("[e2txt]") + "命名风格:{0}", DisplaySelector(rootConfig.E2Txt.NameStyle)); 151 | 152 | rootConfig.Build.Compiler = AnsiConsole.Prompt( 153 | new SelectionPrompt() 154 | .Title("全局默认编译器") 155 | .UseConverter(DisplaySelector) 156 | .AddChoices(Enum.GetValues()) 157 | ); 158 | AnsiConsole.MarkupLine("全局默认编译器:{0}", DisplaySelector(rootConfig.Build.Compiler)); 159 | } 160 | 161 | return rootConfig; 162 | } 163 | 164 | private static string DisplaySelector(T enumValue) 165 | { 166 | return EnumAliasAttribute.GetEnumAliasAttribute(enumValue).Name; 167 | } 168 | } -------------------------------------------------------------------------------- /src/EBuild/Commands/SubCommands/BuildCommand.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | using System.Diagnostics; 3 | using System.Text; 4 | using EBuild.Commands.Base; 5 | using EBuild.Config.Resolved; 6 | using EBuild.Consoles; 7 | using EBuild.Hooks; 8 | using EBuild.Plugins; 9 | using EBuild.Project; 10 | using EBuild.Toolchain; 11 | using Spectre.Console; 12 | using YamlDotNet.Serialization; 13 | 14 | namespace EBuild.Commands.SubCommands; 15 | 16 | [Description(@"构建目标。 17 | 18 | 本命令帮助您编译您配置文件的中的构建目标以及被搜索到的源文件。 19 | 您也可以手动指定要编译的目标或者源文件,如`ebuild build 程序1 程序2`。 20 | 当您不指定编译目标的时候,ebuild默认编译所有目标。")] 21 | public class BuildCommand : TargetCommand 22 | { 23 | private readonly EnvironmentVariables _environmentVariables; 24 | private readonly EclToolchain _eclToolchain; 25 | 26 | public BuildCommand(IDeserializer deserializer, IEnumerable plugins, 27 | EnvironmentVariables environmentVariables, EclToolchain eclToolchain) : base(deserializer, plugins) 28 | { 29 | _environmentVariables = environmentVariables; 30 | _eclToolchain = eclToolchain; 31 | } 32 | 33 | protected override IEnumerable NeededToolchains => new IToolchain[] { _eclToolchain }; 34 | protected override int MaxConcurrencyCount => 1; // 如果多任务并行编译可能会影响 ecl 获取易语言窗口 35 | 36 | protected override IMultiTaskDisplayer GetDisplayer(IList targets) 37 | { 38 | var displayer = new LinesDisplayer() 39 | { 40 | TargetName = target => target.Target.DisplayName(ProjectRoot), 41 | StatusString = e => 42 | { 43 | switch (e) 44 | { 45 | case TargetStatus.Waiting: 46 | return "[grey]{0}[/]"; 47 | case TargetStatus.Skipped: 48 | return "[yellow]{0}[/]"; 49 | case TargetStatus.Doing: 50 | return "{0}"; 51 | case TargetStatus.Done: 52 | return "[green]{0}[/]"; 53 | case TargetStatus.Error: 54 | return "[red]{0}[/]"; 55 | } 56 | 57 | return "{0}"; 58 | } 59 | }; 60 | if (CommandSettings.ConcurrencyCount > MaxConcurrencyCount) 61 | displayer.SetHeader("[grey]目前不支持并行编译目标[/]"); 62 | return displayer; 63 | } 64 | 65 | protected override string GenerateHeading(WholeStatus status) 66 | { 67 | switch (status) 68 | { 69 | case WholeStatus.Doing: 70 | return "[green]正在构建目标...[/]"; 71 | case WholeStatus.Completed: 72 | return "[green]:check_mark: 完成构建[/]"; 73 | case WholeStatus.ErrorOccured: 74 | return "[red]:cross_mark: 构建过程中出现错误[/]"; 75 | } 76 | 77 | return ""; 78 | } 79 | 80 | protected override async Task OnPreDoTargetAsync(ResolvedTarget target, 81 | Action updateTargetStatus, 82 | CancellationToken cancellationToken) 83 | { 84 | if (target.ShouldBuild) 85 | { 86 | Directory.CreateDirectory(Path.GetDirectoryName(target.Target.OutputPath(_resolvedConfig.OutputDir))!); 87 | 88 | var args = GetBuildArgs(target, true); 89 | updateTargetStatus(TargetStatus.Waiting, 90 | $"准备编译中... {Markup.Escape(_eclToolchain.ExecutablePath)} {Markup.Escape(string.Join(" ", args))}"); 91 | } 92 | else 93 | { 94 | updateTargetStatus(TargetStatus.Skipped, "本目标已被排除编译,将被跳过"); 95 | } 96 | 97 | if (target.ShouldBuild) 98 | { 99 | var evs = (EnvironmentVariables)_environmentVariables.Clone(); 100 | SetupEnvironmentVariables(target, evs, Hook.PreBuild); 101 | foreach (var plugin in _plugins) 102 | { 103 | bool shouldContinue = await plugin.OnHook(new PluginContext(_resolvedConfig, evs) 104 | { 105 | BuildTarget = target, 106 | UpdateStatus = (status, log) => updateTargetStatus(status, $"[yellow][[{Hook.PreBuild}]]{log}[/]"), 107 | }, cancellationToken, Hook.PreBuild); 108 | if (!shouldContinue) 109 | { 110 | updateTargetStatus(TargetStatus.Skipped, $"[[{Hook.PreBuild}]]已由构建周期脚本取消该任务!"); 111 | return false; 112 | } 113 | } 114 | } 115 | 116 | return target.ShouldBuild; 117 | } 118 | 119 | private void SetupEnvironmentVariables(ResolvedTarget target, EnvironmentVariables evs, Hook hook) 120 | { 121 | evs.ForProject(ProjectRoot, _resolvedConfig.OutputDir); 122 | evs.AddRange(new[] 123 | { 124 | new EnvironmentVariable("EBUILD_PERIOD", "构建生命周期", hook.ToString), 125 | new EnvironmentVariable("EBUILD_SOURCE_FILE", "被构建的源文件", () => target.Target.Source), 126 | new EnvironmentVariable("EBUILD_TARGET_FILE", "构建目标输出路径", 127 | () => target.Target.OutputPath(_resolvedConfig.OutputDir)), 128 | new EnvironmentVariable("EBUILD_TARGET_TYPE", "构建目标类型", 129 | () => target.SourceMeta?.TargetType.ToString() ?? "(Unkown)"), 130 | }); 131 | } 132 | 133 | private IList GetBuildArgs(ResolvedTarget target, bool hideSecret = false) 134 | { 135 | return EclToolchain.Args(target, _resolvedConfig.OutputDir, hideSecret); 136 | } 137 | 138 | protected override async Task OnDoTargetAsync(ResolvedTarget target, 139 | Action updateTargetStatus, 140 | CancellationToken cancellationToken) 141 | { 142 | updateTargetStatus(TargetStatus.Doing, "开始编译"); 143 | 144 | var compileOk = true; 145 | var handler = GetEclLogHandler(Update, () => compileOk = false); 146 | 147 | var process = new Process(); 148 | foreach (var arg in GetBuildArgs(target)) process.StartInfo.ArgumentList.Add(arg); 149 | process.StartInfo.FileName = _eclToolchain.ExecutablePath; 150 | process.StartInfo.RedirectStandardOutput = true; 151 | process.StartInfo.RedirectStandardError = true; 152 | process.StartInfo.StandardOutputEncoding = 153 | process.StartInfo.StandardErrorEncoding = Encoding.GetEncoding("gbk"); 154 | process.OutputDataReceived += handler; 155 | process.ErrorDataReceived += handler; 156 | process.Start(); 157 | process.BeginOutputReadLine(); 158 | process.BeginErrorReadLine(); 159 | cancellationToken.Register(() => process.Kill()); 160 | await process.WaitForExitAsync(cancellationToken); 161 | 162 | void Update(TargetStatus status, string log) 163 | { 164 | if (!string.IsNullOrEmpty(log)) 165 | { 166 | updateTargetStatus(status, Markup.Escape(log)); 167 | } 168 | } 169 | 170 | var evs = (EnvironmentVariables)_environmentVariables.Clone(); 171 | SetupEnvironmentVariables(target, evs, Hook.PostBuild); 172 | foreach (var plugin in _plugins) 173 | { 174 | await plugin.OnHook(new PluginContext(_resolvedConfig, evs) 175 | { 176 | BuildTarget = target, 177 | UpdateStatus = (status, log) => updateTargetStatus(status, $"[yellow][[{Hook.PostBuild}]]{log}[/]"), 178 | }, cancellationToken, Hook.PostBuild); 179 | } 180 | 181 | if (compileOk) 182 | Update(TargetStatus.Done, $"编译成功,保存到:{target.Target.OutputPath(_resolvedConfig.OutputDir)}"); 183 | 184 | return compileOk; 185 | } 186 | 187 | public static DataReceivedEventHandler GetEclLogHandler(Action update, Action compileFailed) 188 | { 189 | return (sender, e) => 190 | { 191 | if (string.IsNullOrEmpty(e.Data)) 192 | { 193 | return; 194 | } 195 | 196 | if (!EclToolchain.TryMatchError(e.Data, out bool isOk, out int errorCode, out string tip)) 197 | { 198 | update(TargetStatus.Doing, e.Data); 199 | return; 200 | } 201 | 202 | if (!isOk) 203 | { 204 | update(TargetStatus.Error, $"编译目标出错,错误代码:{errorCode},错误提示:{tip}"); 205 | compileFailed(); 206 | } 207 | }; 208 | } 209 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | 3 | .toolchain 4 | *.ecode 5 | 6 | # If you prefer the allow list template instead of the deny list, see community template: 7 | # https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore 8 | # 9 | # Binaries for programs and plugins 10 | *.exe 11 | *.exe~ 12 | *.dll 13 | *.so 14 | *.dylib 15 | 16 | # Output of the go coverage tool, specifically when used with LiteIDE 17 | *.out 18 | 19 | # Dependency directories (remove the comment below to include it) 20 | # vendor/ 21 | 22 | # Go workspace file 23 | go.work### VisualStudio template 24 | ## Ignore Visual Studio temporary files, build results, and 25 | ## files generated by popular Visual Studio add-ons. 26 | ## 27 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 28 | 29 | # User-specific files 30 | *.rsuser 31 | *.suo 32 | *.user 33 | *.userosscache 34 | *.sln.docstates 35 | 36 | # User-specific files (MonoDevelop/Xamarin Studio) 37 | *.userprefs 38 | 39 | # Mono auto generated files 40 | mono_crash.* 41 | 42 | # Build results 43 | [Dd]ebug/ 44 | [Dd]ebugPublic/ 45 | [Rr]elease/ 46 | [Rr]eleases/ 47 | x64/ 48 | x86/ 49 | [Ww][Ii][Nn]32/ 50 | [Aa][Rr][Mm]/ 51 | [Aa][Rr][Mm]64/ 52 | bld/ 53 | [Bb]in/ 54 | [Oo]bj/ 55 | [Ll]og/ 56 | [Ll]ogs/ 57 | 58 | # Visual Studio 2015/2017 cache/options directory 59 | .vs/ 60 | # Uncomment if you have tasks that create the project's static files in wwwroot 61 | #wwwroot/ 62 | 63 | # Visual Studio 2017 auto generated files 64 | Generated\ Files/ 65 | 66 | # MSTest test Results 67 | [Tt]est[Rr]esult*/ 68 | [Bb]uild[Ll]og.* 69 | 70 | # NUnit 71 | *.VisualState.xml 72 | TestResult.xml 73 | nunit-*.xml 74 | 75 | # Build Results of an ATL Project 76 | [Dd]ebugPS/ 77 | [Rr]eleasePS/ 78 | dlldata.c 79 | 80 | # Benchmark Results 81 | BenchmarkDotNet.Artifacts/ 82 | 83 | # .NET Core 84 | project.lock.json 85 | project.fragment.lock.json 86 | artifacts/ 87 | 88 | # ASP.NET Scaffolding 89 | ScaffoldingReadMe.txt 90 | 91 | # StyleCop 92 | StyleCopReport.xml 93 | 94 | # Files built by Visual Studio 95 | *_i.c 96 | *_p.c 97 | *_h.h 98 | *.ilk 99 | *.meta 100 | *.obj 101 | *.iobj 102 | *.pch 103 | *.pdb 104 | *.ipdb 105 | *.pgc 106 | *.pgd 107 | *.rsp 108 | *.sbr 109 | *.tlb 110 | *.tli 111 | *.tlh 112 | *.tmp 113 | *.tmp_proj 114 | *_wpftmp.csproj 115 | *.log 116 | *.vspscc 117 | *.vssscc 118 | .builds 119 | *.pidb 120 | *.svclog 121 | *.scc 122 | 123 | # Chutzpah Test files 124 | _Chutzpah* 125 | 126 | # Visual C++ cache files 127 | ipch/ 128 | *.aps 129 | *.ncb 130 | *.opendb 131 | *.opensdf 132 | *.sdf 133 | *.cachefile 134 | *.VC.db 135 | *.VC.VC.opendb 136 | 137 | # Visual Studio profiler 138 | *.psess 139 | *.vsp 140 | *.vspx 141 | *.sap 142 | 143 | # Visual Studio Trace Files 144 | *.e2e 145 | 146 | # TFS 2012 Local Workspace 147 | $tf/ 148 | 149 | # Guidance Automation Toolkit 150 | *.gpState 151 | 152 | # ReSharper is a .NET coding add-in 153 | _ReSharper*/ 154 | *.[Rr]e[Ss]harper 155 | *.DotSettings.user 156 | 157 | # TeamCity is a build add-in 158 | _TeamCity* 159 | 160 | # DotCover is a Code Coverage Tool 161 | *.dotCover 162 | 163 | # AxoCover is a Code Coverage Tool 164 | .axoCover/* 165 | !.axoCover/settings.json 166 | 167 | # Coverlet is a free, cross platform Code Coverage Tool 168 | coverage*.json 169 | coverage*.xml 170 | coverage*.info 171 | 172 | # Visual Studio code coverage results 173 | *.coverage 174 | *.coveragexml 175 | 176 | # NCrunch 177 | _NCrunch_* 178 | .*crunch*.local.xml 179 | nCrunchTemp_* 180 | 181 | # MightyMoose 182 | *.mm.* 183 | AutoTest.Net/ 184 | 185 | # Web workbench (sass) 186 | .sass-cache/ 187 | 188 | # Installshield output folder 189 | [Ee]xpress/ 190 | 191 | # DocProject is a documentation generator add-in 192 | DocProject/buildhelp/ 193 | DocProject/Help/*.HxT 194 | DocProject/Help/*.HxC 195 | DocProject/Help/*.hhc 196 | DocProject/Help/*.hhk 197 | DocProject/Help/*.hhp 198 | DocProject/Help/Html2 199 | DocProject/Help/html 200 | 201 | # Click-Once directory 202 | publish/ 203 | 204 | # Publish Web Output 205 | *.[Pp]ublish.xml 206 | *.azurePubxml 207 | # Note: Comment the next line if you want to checkin your web deploy settings, 208 | # but database connection strings (with potential passwords) will be unencrypted 209 | *.pubxml 210 | *.publishproj 211 | 212 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 213 | # checkin your Azure Web App publish settings, but sensitive information contained 214 | # in these scripts will be unencrypted 215 | PublishScripts/ 216 | 217 | # NuGet Packages 218 | *.nupkg 219 | # NuGet Symbol Packages 220 | *.snupkg 221 | # The packages folder can be ignored because of Package Restore 222 | **/[Pp]ackages/* 223 | # except build/, which is used as an MSBuild target. 224 | !**/[Pp]ackages/build/ 225 | # Uncomment if necessary however generally it will be regenerated when needed 226 | #!**/[Pp]ackages/repositories.config 227 | # NuGet v3's project.json files produces more ignorable files 228 | *.nuget.props 229 | *.nuget.targets 230 | 231 | # Microsoft Azure Build Output 232 | csx/ 233 | *.build.csdef 234 | 235 | # Microsoft Azure Emulator 236 | ecf/ 237 | rcf/ 238 | 239 | # Windows Store app package directories and files 240 | AppPackages/ 241 | BundleArtifacts/ 242 | Package.StoreAssociation.xml 243 | _pkginfo.txt 244 | *.appx 245 | *.appxbundle 246 | *.appxupload 247 | 248 | # Visual Studio cache files 249 | # files ending in .cache can be ignored 250 | *.[Cc]ache 251 | # but keep track of directories ending in .cache 252 | !?*.[Cc]ache/ 253 | 254 | # Others 255 | ClientBin/ 256 | ~$* 257 | *~ 258 | *.dbmdl 259 | *.dbproj.schemaview 260 | *.jfm 261 | *.pfx 262 | *.publishsettings 263 | orleans.codegen.cs 264 | 265 | # Including strong name files can present a security risk 266 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 267 | #*.snk 268 | 269 | # Since there are multiple workflows, uncomment next line to ignore bower_components 270 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 271 | #bower_components/ 272 | 273 | # RIA/Silverlight projects 274 | Generated_Code/ 275 | 276 | # Backup & report files from converting an old project file 277 | # to a newer Visual Studio version. Backup files are not needed, 278 | # because we have git ;-) 279 | _UpgradeReport_Files/ 280 | Backup*/ 281 | UpgradeLog*.XML 282 | UpgradeLog*.htm 283 | ServiceFabricBackup/ 284 | *.rptproj.bak 285 | 286 | # SQL Server files 287 | *.mdf 288 | *.ldf 289 | *.ndf 290 | 291 | # Business Intelligence projects 292 | *.rdl.data 293 | *.bim.layout 294 | *.bim_*.settings 295 | *.rptproj.rsuser 296 | *- [Bb]ackup.rdl 297 | *- [Bb]ackup ([0-9]).rdl 298 | *- [Bb]ackup ([0-9][0-9]).rdl 299 | 300 | # Microsoft Fakes 301 | FakesAssemblies/ 302 | 303 | # GhostDoc plugin setting file 304 | *.GhostDoc.xml 305 | 306 | # Node.js Tools for Visual Studio 307 | .ntvs_analysis.dat 308 | node_modules/ 309 | 310 | # Visual Studio 6 build log 311 | *.plg 312 | 313 | # Visual Studio 6 workspace options file 314 | *.opt 315 | 316 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 317 | *.vbw 318 | 319 | # Visual Studio LightSwitch build output 320 | **/*.HTMLClient/GeneratedArtifacts 321 | **/*.DesktopClient/GeneratedArtifacts 322 | **/*.DesktopClient/ModelManifest.xml 323 | **/*.Server/GeneratedArtifacts 324 | **/*.Server/ModelManifest.xml 325 | _Pvt_Extensions 326 | 327 | # Paket dependency manager 328 | .paket/paket.exe 329 | paket-files/ 330 | 331 | # FAKE - F# Make 332 | .fake/ 333 | 334 | # CodeRush personal settings 335 | .cr/personal 336 | 337 | # Python Tools for Visual Studio (PTVS) 338 | __pycache__/ 339 | *.pyc 340 | 341 | # Cake - Uncomment if you are using it 342 | # tools/** 343 | # !tools/packages.config 344 | 345 | # Tabs Studio 346 | *.tss 347 | 348 | # Telerik's JustMock configuration file 349 | *.jmconfig 350 | 351 | # BizTalk build output 352 | *.btp.cs 353 | *.btm.cs 354 | *.odx.cs 355 | *.xsd.cs 356 | 357 | # OpenCover UI analysis results 358 | OpenCover/ 359 | 360 | # Azure Stream Analytics local run output 361 | ASALocalRun/ 362 | 363 | # MSBuild Binary and Structured Log 364 | *.binlog 365 | 366 | # NVidia Nsight GPU debugger configuration file 367 | *.nvuser 368 | 369 | # MFractors (Xamarin productivity tool) working folder 370 | .mfractor/ 371 | 372 | # Local History for Visual Studio 373 | .localhistory/ 374 | 375 | # BeatPulse healthcheck temp database 376 | healthchecksdb 377 | 378 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 379 | MigrationBackup/ 380 | 381 | # Ionide (cross platform F# VS Code tools) working folder 382 | .ionide/ 383 | 384 | # Fody - auto-generated XML schema 385 | FodyWeavers.xsd 386 | 387 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@algolia/autocomplete-core@1.7.1": 6 | version "1.7.1" 7 | resolved "https://registry.npmmirror.com/@algolia/autocomplete-core/-/autocomplete-core-1.7.1.tgz#025538b8a9564a9f3dd5bcf8a236d6951c76c7d1" 8 | integrity sha512-eiZw+fxMzNQn01S8dA/hcCpoWCOCwcIIEUtHHdzN5TGB3IpzLbuhqFeTfh2OUhhgkE8Uo17+wH+QJ/wYyQmmzg== 9 | dependencies: 10 | "@algolia/autocomplete-shared" "1.7.1" 11 | 12 | "@algolia/autocomplete-preset-algolia@1.7.1": 13 | version "1.7.1" 14 | resolved "https://registry.npmmirror.com/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.7.1.tgz#7dadc5607097766478014ae2e9e1c9c4b3f957c8" 15 | integrity sha512-pJwmIxeJCymU1M6cGujnaIYcY3QPOVYZOXhFkWVM7IxKzy272BwCvMFMyc5NpG/QmiObBxjo7myd060OeTNJXg== 16 | dependencies: 17 | "@algolia/autocomplete-shared" "1.7.1" 18 | 19 | "@algolia/autocomplete-shared@1.7.1": 20 | version "1.7.1" 21 | resolved "https://registry.npmmirror.com/@algolia/autocomplete-shared/-/autocomplete-shared-1.7.1.tgz#95c3a0b4b78858fed730cf9c755b7d1cd0c82c74" 22 | integrity sha512-eTmGVqY3GeyBTT8IWiB2K5EuURAqhnumfktAEoHxfDY2o7vg2rSnO16ZtIG0fMgt3py28Vwgq42/bVEuaQV7pg== 23 | 24 | "@algolia/cache-browser-local-storage@4.14.2": 25 | version "4.14.2" 26 | resolved "https://registry.npmmirror.com/@algolia/cache-browser-local-storage/-/cache-browser-local-storage-4.14.2.tgz#d5b1b90130ca87c6321de876e167df9ec6524936" 27 | integrity sha512-FRweBkK/ywO+GKYfAWbrepewQsPTIEirhi1BdykX9mxvBPtGNKccYAxvGdDCumU1jL4r3cayio4psfzKMejBlA== 28 | dependencies: 29 | "@algolia/cache-common" "4.14.2" 30 | 31 | "@algolia/cache-common@4.14.2": 32 | version "4.14.2" 33 | resolved "https://registry.npmmirror.com/@algolia/cache-common/-/cache-common-4.14.2.tgz#b946b6103c922f0c06006fb6929163ed2c67d598" 34 | integrity sha512-SbvAlG9VqNanCErr44q6lEKD2qoK4XtFNx9Qn8FK26ePCI8I9yU7pYB+eM/cZdS9SzQCRJBbHUumVr4bsQ4uxg== 35 | 36 | "@algolia/cache-in-memory@4.14.2": 37 | version "4.14.2" 38 | resolved "https://registry.npmmirror.com/@algolia/cache-in-memory/-/cache-in-memory-4.14.2.tgz#88e4a21474f9ac05331c2fa3ceb929684a395a24" 39 | integrity sha512-HrOukWoop9XB/VFojPv1R5SVXowgI56T9pmezd/djh2JnVN/vXswhXV51RKy4nCpqxyHt/aGFSq2qkDvj6KiuQ== 40 | dependencies: 41 | "@algolia/cache-common" "4.14.2" 42 | 43 | "@algolia/client-account@4.14.2": 44 | version "4.14.2" 45 | resolved "https://registry.npmmirror.com/@algolia/client-account/-/client-account-4.14.2.tgz#b76ac1ba9ea71e8c3f77a1805b48350dc0728a16" 46 | integrity sha512-WHtriQqGyibbb/Rx71YY43T0cXqyelEU0lB2QMBRXvD2X0iyeGl4qMxocgEIcbHyK7uqE7hKgjT8aBrHqhgc1w== 47 | dependencies: 48 | "@algolia/client-common" "4.14.2" 49 | "@algolia/client-search" "4.14.2" 50 | "@algolia/transporter" "4.14.2" 51 | 52 | "@algolia/client-analytics@4.14.2": 53 | version "4.14.2" 54 | resolved "https://registry.npmmirror.com/@algolia/client-analytics/-/client-analytics-4.14.2.tgz#ca04dcaf9a78ee5c92c5cb5e9c74cf031eb2f1fb" 55 | integrity sha512-yBvBv2mw+HX5a+aeR0dkvUbFZsiC4FKSnfqk9rrfX+QrlNOKEhCG0tJzjiOggRW4EcNqRmaTULIYvIzQVL2KYQ== 56 | dependencies: 57 | "@algolia/client-common" "4.14.2" 58 | "@algolia/client-search" "4.14.2" 59 | "@algolia/requester-common" "4.14.2" 60 | "@algolia/transporter" "4.14.2" 61 | 62 | "@algolia/client-common@4.14.2": 63 | version "4.14.2" 64 | resolved "https://registry.npmmirror.com/@algolia/client-common/-/client-common-4.14.2.tgz#e1324e167ffa8af60f3e8bcd122110fd0bfd1300" 65 | integrity sha512-43o4fslNLcktgtDMVaT5XwlzsDPzlqvqesRi4MjQz2x4/Sxm7zYg5LRYFol1BIhG6EwxKvSUq8HcC/KxJu3J0Q== 66 | dependencies: 67 | "@algolia/requester-common" "4.14.2" 68 | "@algolia/transporter" "4.14.2" 69 | 70 | "@algolia/client-personalization@4.14.2": 71 | version "4.14.2" 72 | resolved "https://registry.npmmirror.com/@algolia/client-personalization/-/client-personalization-4.14.2.tgz#656bbb6157a3dd1a4be7de65e457fda136c404ec" 73 | integrity sha512-ACCoLi0cL8CBZ1W/2juehSltrw2iqsQBnfiu/Rbl9W2yE6o2ZUb97+sqN/jBqYNQBS+o0ekTMKNkQjHHAcEXNw== 74 | dependencies: 75 | "@algolia/client-common" "4.14.2" 76 | "@algolia/requester-common" "4.14.2" 77 | "@algolia/transporter" "4.14.2" 78 | 79 | "@algolia/client-search@4.14.2": 80 | version "4.14.2" 81 | resolved "https://registry.npmmirror.com/@algolia/client-search/-/client-search-4.14.2.tgz#357bdb7e640163f0e33bad231dfcc21f67dc2e92" 82 | integrity sha512-L5zScdOmcZ6NGiVbLKTvP02UbxZ0njd5Vq9nJAmPFtjffUSOGEp11BmD2oMJ5QvARgx2XbX4KzTTNS5ECYIMWw== 83 | dependencies: 84 | "@algolia/client-common" "4.14.2" 85 | "@algolia/requester-common" "4.14.2" 86 | "@algolia/transporter" "4.14.2" 87 | 88 | "@algolia/logger-common@4.14.2": 89 | version "4.14.2" 90 | resolved "https://registry.npmmirror.com/@algolia/logger-common/-/logger-common-4.14.2.tgz#b74b3a92431f92665519d95942c246793ec390ee" 91 | integrity sha512-/JGlYvdV++IcMHBnVFsqEisTiOeEr6cUJtpjz8zc0A9c31JrtLm318Njc72p14Pnkw3A/5lHHh+QxpJ6WFTmsA== 92 | 93 | "@algolia/logger-console@4.14.2": 94 | version "4.14.2" 95 | resolved "https://registry.npmmirror.com/@algolia/logger-console/-/logger-console-4.14.2.tgz#ec49cb47408f5811d4792598683923a800abce7b" 96 | integrity sha512-8S2PlpdshbkwlLCSAB5f8c91xyc84VM9Ar9EdfE9UmX+NrKNYnWR1maXXVDQQoto07G1Ol/tYFnFVhUZq0xV/g== 97 | dependencies: 98 | "@algolia/logger-common" "4.14.2" 99 | 100 | "@algolia/requester-browser-xhr@4.14.2": 101 | version "4.14.2" 102 | resolved "https://registry.npmmirror.com/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.14.2.tgz#a2cd4d9d8d90d53109cc7f3682dc6ebf20f798f2" 103 | integrity sha512-CEh//xYz/WfxHFh7pcMjQNWgpl4wFB85lUMRyVwaDPibNzQRVcV33YS+63fShFWc2+42YEipFGH2iPzlpszmDw== 104 | dependencies: 105 | "@algolia/requester-common" "4.14.2" 106 | 107 | "@algolia/requester-common@4.14.2": 108 | version "4.14.2" 109 | resolved "https://registry.npmmirror.com/@algolia/requester-common/-/requester-common-4.14.2.tgz#bc4e9e5ee16c953c0ecacbfb334a33c30c28b1a1" 110 | integrity sha512-73YQsBOKa5fvVV3My7iZHu1sUqmjjfs9TteFWwPwDmnad7T0VTCopttcsM3OjLxZFtBnX61Xxl2T2gmG2O4ehg== 111 | 112 | "@algolia/requester-node-http@4.14.2": 113 | version "4.14.2" 114 | resolved "https://registry.npmmirror.com/@algolia/requester-node-http/-/requester-node-http-4.14.2.tgz#7c1223a1785decaab1def64c83dade6bea45e115" 115 | integrity sha512-oDbb02kd1o5GTEld4pETlPZLY0e+gOSWjWMJHWTgDXbv9rm/o2cF7japO6Vj1ENnrqWvLBmW1OzV9g6FUFhFXg== 116 | dependencies: 117 | "@algolia/requester-common" "4.14.2" 118 | 119 | "@algolia/transporter@4.14.2": 120 | version "4.14.2" 121 | resolved "https://registry.npmmirror.com/@algolia/transporter/-/transporter-4.14.2.tgz#77c069047fb1a4359ee6a51f51829508e44a1e3d" 122 | integrity sha512-t89dfQb2T9MFQHidjHcfhh6iGMNwvuKUvojAj+JsrHAGbuSy7yE4BylhLX6R0Q1xYRoC4Vvv+O5qIw/LdnQfsQ== 123 | dependencies: 124 | "@algolia/cache-common" "4.14.2" 125 | "@algolia/logger-common" "4.14.2" 126 | "@algolia/requester-common" "4.14.2" 127 | 128 | "@babel/parser@^7.16.4": 129 | version "7.18.9" 130 | resolved "https://registry.npmmirror.com/@babel/parser/-/parser-7.18.9.tgz#f2dde0c682ccc264a9a8595efd030a5cc8fd2539" 131 | integrity sha512-9uJveS9eY9DJ0t64YbIBZICtJy8a5QrDEVdiLCG97fVLpDTpGX7t8mMSb6OWw6Lrnjqj4O8zwjELX3dhoMgiBg== 132 | 133 | "@docsearch/css@3.1.1", "@docsearch/css@^3.0.0": 134 | version "3.1.1" 135 | resolved "https://registry.npmmirror.com/@docsearch/css/-/css-3.1.1.tgz#e0976bf995e383f8ee8657306311b9cb95016330" 136 | integrity sha512-utLgg7E1agqQeqCJn05DWC7XXMk4tMUUnL7MZupcknRu2OzGN13qwey2qA/0NAKkVBGugiWtON0+rlU0QIPojg== 137 | 138 | "@docsearch/js@^3.0.0": 139 | version "3.1.1" 140 | resolved "https://registry.npmmirror.com/@docsearch/js/-/js-3.1.1.tgz#fbcf85d034b11ae15397310ef117c7d6fb4e6871" 141 | integrity sha512-bt7l2aKRoSnLUuX+s4LVQ1a7AF2c9myiZNv5uvQCePG5tpvVGpwrnMwqVXOUJn9q6FwVVhOrQMO/t+QmnnAEUw== 142 | dependencies: 143 | "@docsearch/react" "3.1.1" 144 | preact "^10.0.0" 145 | 146 | "@docsearch/react@3.1.1": 147 | version "3.1.1" 148 | resolved "https://registry.npmmirror.com/@docsearch/react/-/react-3.1.1.tgz#3dffb5db8cf9eb95d6e732cf038264bfc10191ed" 149 | integrity sha512-cfoql4qvtsVRqBMYxhlGNpvyy/KlCoPqjIsJSZYqYf9AplZncKjLBTcwBu6RXFMVCe30cIFljniI4OjqAU67pQ== 150 | dependencies: 151 | "@algolia/autocomplete-core" "1.7.1" 152 | "@algolia/autocomplete-preset-algolia" "1.7.1" 153 | "@docsearch/css" "3.1.1" 154 | algoliasearch "^4.0.0" 155 | 156 | "@types/linkify-it@*": 157 | version "3.0.2" 158 | resolved "https://registry.npmmirror.com/@types/linkify-it/-/linkify-it-3.0.2.tgz#fd2cd2edbaa7eaac7e7f3c1748b52a19143846c9" 159 | integrity sha512-HZQYqbiFVWufzCwexrvh694SOim8z2d+xJl5UNamcvQFejLY/2YUtzXHYi3cHdI7PMlS8ejH2slRAOJQ32aNbA== 160 | 161 | "@types/markdown-it@^12.2.3": 162 | version "12.2.3" 163 | resolved "https://registry.npmmirror.com/@types/markdown-it/-/markdown-it-12.2.3.tgz#0d6f6e5e413f8daaa26522904597be3d6cd93b51" 164 | integrity sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ== 165 | dependencies: 166 | "@types/linkify-it" "*" 167 | "@types/mdurl" "*" 168 | 169 | "@types/mdurl@*": 170 | version "1.0.2" 171 | resolved "https://registry.npmmirror.com/@types/mdurl/-/mdurl-1.0.2.tgz#e2ce9d83a613bacf284c7be7d491945e39e1f8e9" 172 | integrity sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA== 173 | 174 | "@types/web-bluetooth@^0.0.14": 175 | version "0.0.14" 176 | resolved "https://registry.npmmirror.com/@types/web-bluetooth/-/web-bluetooth-0.0.14.tgz#94e175b53623384bff1f354cdb3197a8d63cdbe5" 177 | integrity sha512-5d2RhCard1nQUC3aHcq/gHzWYO6K0WJmAbjO7mQJgCQKtZpgXxv1rOM6O/dBDhDYYVutk1sciOgNSe+5YyfM8A== 178 | 179 | "@vitejs/plugin-vue@^2.3.2": 180 | version "2.3.3" 181 | resolved "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-2.3.3.tgz#fbf80cc039b82ac21a1acb0f0478de8f61fbf600" 182 | integrity sha512-SmQLDyhz+6lGJhPELsBdzXGc+AcaT8stgkbiTFGpXPe8Tl1tJaBw1A6pxDqDuRsVkD8uscrkx3hA7QDOoKYtyw== 183 | 184 | "@vue/compiler-core@3.2.37": 185 | version "3.2.37" 186 | resolved "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.2.37.tgz#b3c42e04c0e0f2c496ff1784e543fbefe91e215a" 187 | integrity sha512-81KhEjo7YAOh0vQJoSmAD68wLfYqJvoiD4ulyedzF+OEk/bk6/hx3fTNVfuzugIIaTrOx4PGx6pAiBRe5e9Zmg== 188 | dependencies: 189 | "@babel/parser" "^7.16.4" 190 | "@vue/shared" "3.2.37" 191 | estree-walker "^2.0.2" 192 | source-map "^0.6.1" 193 | 194 | "@vue/compiler-dom@3.2.37": 195 | version "3.2.37" 196 | resolved "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.2.37.tgz#10d2427a789e7c707c872da9d678c82a0c6582b5" 197 | integrity sha512-yxJLH167fucHKxaqXpYk7x8z7mMEnXOw3G2q62FTkmsvNxu4FQSu5+3UMb+L7fjKa26DEzhrmCxAgFLLIzVfqQ== 198 | dependencies: 199 | "@vue/compiler-core" "3.2.37" 200 | "@vue/shared" "3.2.37" 201 | 202 | "@vue/compiler-sfc@3.2.37": 203 | version "3.2.37" 204 | resolved "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.2.37.tgz#3103af3da2f40286edcd85ea495dcb35bc7f5ff4" 205 | integrity sha512-+7i/2+9LYlpqDv+KTtWhOZH+pa8/HnX/905MdVmAcI/mPQOBwkHHIzrsEsucyOIZQYMkXUiTkmZq5am/NyXKkg== 206 | dependencies: 207 | "@babel/parser" "^7.16.4" 208 | "@vue/compiler-core" "3.2.37" 209 | "@vue/compiler-dom" "3.2.37" 210 | "@vue/compiler-ssr" "3.2.37" 211 | "@vue/reactivity-transform" "3.2.37" 212 | "@vue/shared" "3.2.37" 213 | estree-walker "^2.0.2" 214 | magic-string "^0.25.7" 215 | postcss "^8.1.10" 216 | source-map "^0.6.1" 217 | 218 | "@vue/compiler-ssr@3.2.37": 219 | version "3.2.37" 220 | resolved "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.2.37.tgz#4899d19f3a5fafd61524a9d1aee8eb0505313cff" 221 | integrity sha512-7mQJD7HdXxQjktmsWp/J67lThEIcxLemz1Vb5I6rYJHR5vI+lON3nPGOH3ubmbvYGt8xEUaAr1j7/tIFWiEOqw== 222 | dependencies: 223 | "@vue/compiler-dom" "3.2.37" 224 | "@vue/shared" "3.2.37" 225 | 226 | "@vue/devtools-api@^6.1.4": 227 | version "6.2.1" 228 | resolved "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-6.2.1.tgz#6f2948ff002ec46df01420dfeff91de16c5b4092" 229 | integrity sha512-OEgAMeQXvCoJ+1x8WyQuVZzFo0wcyCmUR3baRVLmKBo1LmYZWMlRiXlux5jd0fqVJu6PfDbOrZItVqUEzLobeQ== 230 | 231 | "@vue/reactivity-transform@3.2.37": 232 | version "3.2.37" 233 | resolved "https://registry.npmmirror.com/@vue/reactivity-transform/-/reactivity-transform-3.2.37.tgz#0caa47c4344df4ae59f5a05dde2a8758829f8eca" 234 | integrity sha512-IWopkKEb+8qpu/1eMKVeXrK0NLw9HicGviJzhJDEyfxTR9e1WtpnnbYkJWurX6WwoFP0sz10xQg8yL8lgskAZg== 235 | dependencies: 236 | "@babel/parser" "^7.16.4" 237 | "@vue/compiler-core" "3.2.37" 238 | "@vue/shared" "3.2.37" 239 | estree-walker "^2.0.2" 240 | magic-string "^0.25.7" 241 | 242 | "@vue/reactivity@3.2.37": 243 | version "3.2.37" 244 | resolved "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.2.37.tgz#5bc3847ac58828e2b78526e08219e0a1089f8848" 245 | integrity sha512-/7WRafBOshOc6m3F7plwzPeCu/RCVv9uMpOwa/5PiY1Zz+WLVRWiy0MYKwmg19KBdGtFWsmZ4cD+LOdVPcs52A== 246 | dependencies: 247 | "@vue/shared" "3.2.37" 248 | 249 | "@vue/runtime-core@3.2.37": 250 | version "3.2.37" 251 | resolved "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.2.37.tgz#7ba7c54bb56e5d70edfc2f05766e1ca8519966e3" 252 | integrity sha512-JPcd9kFyEdXLl/i0ClS7lwgcs0QpUAWj+SKX2ZC3ANKi1U4DOtiEr6cRqFXsPwY5u1L9fAjkinIdB8Rz3FoYNQ== 253 | dependencies: 254 | "@vue/reactivity" "3.2.37" 255 | "@vue/shared" "3.2.37" 256 | 257 | "@vue/runtime-dom@3.2.37": 258 | version "3.2.37" 259 | resolved "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.2.37.tgz#002bdc8228fa63949317756fb1e92cdd3f9f4bbd" 260 | integrity sha512-HimKdh9BepShW6YozwRKAYjYQWg9mQn63RGEiSswMbW+ssIht1MILYlVGkAGGQbkhSh31PCdoUcfiu4apXJoPw== 261 | dependencies: 262 | "@vue/runtime-core" "3.2.37" 263 | "@vue/shared" "3.2.37" 264 | csstype "^2.6.8" 265 | 266 | "@vue/server-renderer@3.2.37": 267 | version "3.2.37" 268 | resolved "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.2.37.tgz#840a29c8dcc29bddd9b5f5ffa22b95c0e72afdfc" 269 | integrity sha512-kLITEJvaYgZQ2h47hIzPh2K3jG8c1zCVbp/o/bzQOyvzaKiCquKS7AaioPI28GNxIsE/zSx+EwWYsNxDCX95MA== 270 | dependencies: 271 | "@vue/compiler-ssr" "3.2.37" 272 | "@vue/shared" "3.2.37" 273 | 274 | "@vue/shared@3.2.37": 275 | version "3.2.37" 276 | resolved "https://registry.npmmirror.com/@vue/shared/-/shared-3.2.37.tgz#8e6adc3f2759af52f0e85863dfb0b711ecc5c702" 277 | integrity sha512-4rSJemR2NQIo9Klm1vabqWjD8rs/ZaJSzMxkMNeJS6lHiUjjUeYFbooN19NgFjztubEKh3WlZUeOLVdbbUWHsw== 278 | 279 | "@vueuse/core@^8.5.0": 280 | version "8.9.4" 281 | resolved "https://registry.npmmirror.com/@vueuse/core/-/core-8.9.4.tgz#c7db40f19390b3c9f4ff9294a30461497f62ec19" 282 | integrity sha512-B/Mdj9TK1peFyWaPof+Zf/mP9XuGAngaJZBwPaXBvU3aCTZlx3ltlrFFFyMV4iGBwsjSCeUCgZrtkEj9dS2Y3Q== 283 | dependencies: 284 | "@types/web-bluetooth" "^0.0.14" 285 | "@vueuse/metadata" "8.9.4" 286 | "@vueuse/shared" "8.9.4" 287 | vue-demi "*" 288 | 289 | "@vueuse/metadata@8.9.4": 290 | version "8.9.4" 291 | resolved "https://registry.npmmirror.com/@vueuse/metadata/-/metadata-8.9.4.tgz#a4132db33e4c1b1023636acfa20aa7b37ab3d978" 292 | integrity sha512-IwSfzH80bnJMzqhaapqJl9JRIiyQU0zsRGEgnxN6jhq7992cPUJIRfV+JHRIZXjYqbwt07E1gTEp0R0zPJ1aqw== 293 | 294 | "@vueuse/shared@8.9.4": 295 | version "8.9.4" 296 | resolved "https://registry.npmmirror.com/@vueuse/shared/-/shared-8.9.4.tgz#c9741c30ffb666b50d62f0dd80b76119fd47573e" 297 | integrity sha512-wt+T30c4K6dGRMVqPddexEVLa28YwxW5OFIPmzUHICjphfAuBFTTdDoyqREZNDOFJZ44ARH1WWQNCUK8koJ+Ag== 298 | dependencies: 299 | vue-demi "*" 300 | 301 | algoliasearch@^4.0.0: 302 | version "4.14.2" 303 | resolved "https://registry.npmmirror.com/algoliasearch/-/algoliasearch-4.14.2.tgz#63f142583bfc3a9bd3cd4a1b098bf6fe58e56f6c" 304 | integrity sha512-ngbEQonGEmf8dyEh5f+uOIihv4176dgbuOZspiuhmTTBRBuzWu3KCGHre6uHj5YyuC7pNvQGzB6ZNJyZi0z+Sg== 305 | dependencies: 306 | "@algolia/cache-browser-local-storage" "4.14.2" 307 | "@algolia/cache-common" "4.14.2" 308 | "@algolia/cache-in-memory" "4.14.2" 309 | "@algolia/client-account" "4.14.2" 310 | "@algolia/client-analytics" "4.14.2" 311 | "@algolia/client-common" "4.14.2" 312 | "@algolia/client-personalization" "4.14.2" 313 | "@algolia/client-search" "4.14.2" 314 | "@algolia/logger-common" "4.14.2" 315 | "@algolia/logger-console" "4.14.2" 316 | "@algolia/requester-browser-xhr" "4.14.2" 317 | "@algolia/requester-common" "4.14.2" 318 | "@algolia/requester-node-http" "4.14.2" 319 | "@algolia/transporter" "4.14.2" 320 | 321 | body-scroll-lock@^4.0.0-beta.0: 322 | version "4.0.0-beta.0" 323 | resolved "https://registry.npmmirror.com/body-scroll-lock/-/body-scroll-lock-4.0.0-beta.0.tgz#4f78789d10e6388115c0460cd6d7d4dd2bbc4f7e" 324 | integrity sha512-a7tP5+0Mw3YlUJcGAKUqIBkYYGlYxk2fnCasq/FUph1hadxlTRjF+gAcZksxANnaMnALjxEddmSi/H3OR8ugcQ== 325 | 326 | csstype@^2.6.8: 327 | version "2.6.20" 328 | resolved "https://registry.npmmirror.com/csstype/-/csstype-2.6.20.tgz#9229c65ea0b260cf4d3d997cb06288e36a8d6dda" 329 | integrity sha512-/WwNkdXfckNgw6S5R125rrW8ez139lBHWouiBvX8dfMFtcn6V81REDqnH7+CRpRipfYlyU1CmOnOxrmGcFOjeA== 330 | 331 | esbuild-android-64@0.14.51: 332 | version "0.14.51" 333 | resolved "https://registry.npmmirror.com/esbuild-android-64/-/esbuild-android-64-0.14.51.tgz#414a087cb0de8db1e347ecca6c8320513de433db" 334 | integrity sha512-6FOuKTHnC86dtrKDmdSj2CkcKF8PnqkaIXqvgydqfJmqBazCPdw+relrMlhGjkvVdiiGV70rpdnyFmA65ekBCQ== 335 | 336 | esbuild-android-arm64@0.14.51: 337 | version "0.14.51" 338 | resolved "https://registry.npmmirror.com/esbuild-android-arm64/-/esbuild-android-arm64-0.14.51.tgz#55de3bce2aab72bcd2b606da4318ad00fb9c8151" 339 | integrity sha512-vBtp//5VVkZWmYYvHsqBRCMMi1MzKuMIn5XDScmnykMTu9+TD9v0NMEDqQxvtFToeYmojdo5UCV2vzMQWJcJ4A== 340 | 341 | esbuild-darwin-64@0.14.51: 342 | version "0.14.51" 343 | resolved "https://registry.npmmirror.com/esbuild-darwin-64/-/esbuild-darwin-64-0.14.51.tgz#4259f23ed6b4cea2ec8a28d87b7fb9801f093754" 344 | integrity sha512-YFmXPIOvuagDcwCejMRtCDjgPfnDu+bNeh5FU2Ryi68ADDVlWEpbtpAbrtf/lvFTWPexbgyKgzppNgsmLPr8PA== 345 | 346 | esbuild-darwin-arm64@0.14.51: 347 | version "0.14.51" 348 | resolved "https://registry.npmmirror.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.51.tgz#d77b4366a71d84e530ba019d540b538b295d494a" 349 | integrity sha512-juYD0QnSKwAMfzwKdIF6YbueXzS6N7y4GXPDeDkApz/1RzlT42mvX9jgNmyOlWKN7YzQAYbcUEJmZJYQGdf2ow== 350 | 351 | esbuild-freebsd-64@0.14.51: 352 | version "0.14.51" 353 | resolved "https://registry.npmmirror.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.51.tgz#27b6587b3639f10519c65e07219d249b01f2ad38" 354 | integrity sha512-cLEI/aXjb6vo5O2Y8rvVSQ7smgLldwYY5xMxqh/dQGfWO+R1NJOFsiax3IS4Ng300SVp7Gz3czxT6d6qf2cw0g== 355 | 356 | esbuild-freebsd-arm64@0.14.51: 357 | version "0.14.51" 358 | resolved "https://registry.npmmirror.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.51.tgz#63c435917e566808c71fafddc600aca4d78be1ec" 359 | integrity sha512-TcWVw/rCL2F+jUgRkgLa3qltd5gzKjIMGhkVybkjk6PJadYInPtgtUBp1/hG+mxyigaT7ib+od1Xb84b+L+1Mg== 360 | 361 | esbuild-linux-32@0.14.51: 362 | version "0.14.51" 363 | resolved "https://registry.npmmirror.com/esbuild-linux-32/-/esbuild-linux-32-0.14.51.tgz#c3da774143a37e7f11559b9369d98f11f997a5d9" 364 | integrity sha512-RFqpyC5ChyWrjx8Xj2K0EC1aN0A37H6OJfmUXIASEqJoHcntuV3j2Efr9RNmUhMfNE6yEj2VpYuDteZLGDMr0w== 365 | 366 | esbuild-linux-64@0.14.51: 367 | version "0.14.51" 368 | resolved "https://registry.npmmirror.com/esbuild-linux-64/-/esbuild-linux-64-0.14.51.tgz#5d92b67f674e02ae0b4a9de9a757ba482115c4ae" 369 | integrity sha512-dxjhrqo5i7Rq6DXwz5v+MEHVs9VNFItJmHBe1CxROWNf4miOGoQhqSG8StStbDkQ1Mtobg6ng+4fwByOhoQoeA== 370 | 371 | esbuild-linux-arm64@0.14.51: 372 | version "0.14.51" 373 | resolved "https://registry.npmmirror.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.51.tgz#dac84740516e859d8b14e1ecc478dd5241b10c93" 374 | integrity sha512-D9rFxGutoqQX3xJPxqd6o+kvYKeIbM0ifW2y0bgKk5HPgQQOo2k9/2Vpto3ybGYaFPCE5qTGtqQta9PoP6ZEzw== 375 | 376 | esbuild-linux-arm@0.14.51: 377 | version "0.14.51" 378 | resolved "https://registry.npmmirror.com/esbuild-linux-arm/-/esbuild-linux-arm-0.14.51.tgz#b3ae7000696cd53ed95b2b458554ff543a60e106" 379 | integrity sha512-LsJynDxYF6Neg7ZC7748yweCDD+N8ByCv22/7IAZglIEniEkqdF4HCaa49JNDLw1UQGlYuhOB8ZT/MmcSWzcWg== 380 | 381 | esbuild-linux-mips64le@0.14.51: 382 | version "0.14.51" 383 | resolved "https://registry.npmmirror.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.51.tgz#dad10770fac94efa092b5a0643821c955a9dd385" 384 | integrity sha512-vS54wQjy4IinLSlb5EIlLoln8buh1yDgliP4CuEHumrPk4PvvP4kTRIG4SzMXm6t19N0rIfT4bNdAxzJLg2k6A== 385 | 386 | esbuild-linux-ppc64le@0.14.51: 387 | version "0.14.51" 388 | resolved "https://registry.npmmirror.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.51.tgz#b68c2f8294d012a16a88073d67e976edd4850ae0" 389 | integrity sha512-xcdd62Y3VfGoyphNP/aIV9LP+RzFw5M5Z7ja+zdpQHHvokJM7d0rlDRMN+iSSwvUymQkqZO+G/xjb4/75du8BQ== 390 | 391 | esbuild-linux-riscv64@0.14.51: 392 | version "0.14.51" 393 | resolved "https://registry.npmmirror.com/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.51.tgz#608a318b8697123e44c1e185cdf6708e3df50b93" 394 | integrity sha512-syXHGak9wkAnFz0gMmRBoy44JV0rp4kVCEA36P5MCeZcxFq8+fllBC2t6sKI23w3qd8Vwo9pTADCgjTSf3L3rA== 395 | 396 | esbuild-linux-s390x@0.14.51: 397 | version "0.14.51" 398 | resolved "https://registry.npmmirror.com/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.51.tgz#c9e7791170a3295dba79b93aa452beb9838a8625" 399 | integrity sha512-kFAJY3dv+Wq8o28K/C7xkZk/X34rgTwhknSsElIqoEo8armCOjMJ6NsMxm48KaWY2h2RUYGtQmr+RGuUPKBhyw== 400 | 401 | esbuild-netbsd-64@0.14.51: 402 | version "0.14.51" 403 | resolved "https://registry.npmmirror.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.51.tgz#0abd40b8c2e37fda6f5cc41a04cb2b690823d891" 404 | integrity sha512-ZZBI7qrR1FevdPBVHz/1GSk1x5GDL/iy42Zy8+neEm/HA7ma+hH/bwPEjeHXKWUDvM36CZpSL/fn1/y9/Hb+1A== 405 | 406 | esbuild-openbsd-64@0.14.51: 407 | version "0.14.51" 408 | resolved "https://registry.npmmirror.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.51.tgz#4adba0b7ea7eb1428bb00d8e94c199a949b130e8" 409 | integrity sha512-7R1/p39M+LSVQVgDVlcY1KKm6kFKjERSX1lipMG51NPcspJD1tmiZSmmBXoY5jhHIu6JL1QkFDTx94gMYK6vfA== 410 | 411 | esbuild-sunos-64@0.14.51: 412 | version "0.14.51" 413 | resolved "https://registry.npmmirror.com/esbuild-sunos-64/-/esbuild-sunos-64-0.14.51.tgz#4b8a6d97dfedda30a6e39607393c5c90ebf63891" 414 | integrity sha512-HoHaCswHxLEYN8eBTtyO0bFEWvA3Kdb++hSQ/lLG7TyKF69TeSG0RNoBRAs45x/oCeWaTDntEZlYwAfQlhEtJA== 415 | 416 | esbuild-windows-32@0.14.51: 417 | version "0.14.51" 418 | resolved "https://registry.npmmirror.com/esbuild-windows-32/-/esbuild-windows-32-0.14.51.tgz#d31d8ca0c1d314fb1edea163685a423b62e9ac17" 419 | integrity sha512-4rtwSAM35A07CBt1/X8RWieDj3ZUHQqUOaEo5ZBs69rt5WAFjP4aqCIobdqOy4FdhYw1yF8Z0xFBTyc9lgPtEg== 420 | 421 | esbuild-windows-64@0.14.51: 422 | version "0.14.51" 423 | resolved "https://registry.npmmirror.com/esbuild-windows-64/-/esbuild-windows-64-0.14.51.tgz#7d3c09c8652d222925625637bdc7e6c223e0085d" 424 | integrity sha512-HoN/5HGRXJpWODprGCgKbdMvrC3A2gqvzewu2eECRw2sYxOUoh2TV1tS+G7bHNapPGI79woQJGV6pFH7GH7qnA== 425 | 426 | esbuild-windows-arm64@0.14.51: 427 | version "0.14.51" 428 | resolved "https://registry.npmmirror.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.51.tgz#0220d2304bfdc11bc27e19b2aaf56edf183e4ae9" 429 | integrity sha512-JQDqPjuOH7o+BsKMSddMfmVJXrnYZxXDHsoLHc0xgmAZkOOCflRmC43q31pk79F9xuyWY45jDBPolb5ZgGOf9g== 430 | 431 | esbuild@^0.14.27: 432 | version "0.14.51" 433 | resolved "https://registry.npmmirror.com/esbuild/-/esbuild-0.14.51.tgz#1c8ecbc8db3710da03776211dc3ee3448f7aa51e" 434 | integrity sha512-+CvnDitD7Q5sT7F+FM65sWkF8wJRf+j9fPcprxYV4j+ohmzVj2W7caUqH2s5kCaCJAfcAICjSlKhDCcvDpU7nw== 435 | optionalDependencies: 436 | esbuild-android-64 "0.14.51" 437 | esbuild-android-arm64 "0.14.51" 438 | esbuild-darwin-64 "0.14.51" 439 | esbuild-darwin-arm64 "0.14.51" 440 | esbuild-freebsd-64 "0.14.51" 441 | esbuild-freebsd-arm64 "0.14.51" 442 | esbuild-linux-32 "0.14.51" 443 | esbuild-linux-64 "0.14.51" 444 | esbuild-linux-arm "0.14.51" 445 | esbuild-linux-arm64 "0.14.51" 446 | esbuild-linux-mips64le "0.14.51" 447 | esbuild-linux-ppc64le "0.14.51" 448 | esbuild-linux-riscv64 "0.14.51" 449 | esbuild-linux-s390x "0.14.51" 450 | esbuild-netbsd-64 "0.14.51" 451 | esbuild-openbsd-64 "0.14.51" 452 | esbuild-sunos-64 "0.14.51" 453 | esbuild-windows-32 "0.14.51" 454 | esbuild-windows-64 "0.14.51" 455 | esbuild-windows-arm64 "0.14.51" 456 | 457 | estree-walker@^2.0.2: 458 | version "2.0.2" 459 | resolved "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac" 460 | integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w== 461 | 462 | fsevents@~2.3.2: 463 | version "2.3.2" 464 | resolved "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" 465 | integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== 466 | 467 | function-bind@^1.1.1: 468 | version "1.1.1" 469 | resolved "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" 470 | integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== 471 | 472 | has@^1.0.3: 473 | version "1.0.3" 474 | resolved "https://registry.npmmirror.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" 475 | integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== 476 | dependencies: 477 | function-bind "^1.1.1" 478 | 479 | is-core-module@^2.9.0: 480 | version "2.9.0" 481 | resolved "https://registry.npmmirror.com/is-core-module/-/is-core-module-2.9.0.tgz#e1c34429cd51c6dd9e09e0799e396e27b19a9c69" 482 | integrity sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A== 483 | dependencies: 484 | has "^1.0.3" 485 | 486 | jsonc-parser@^3.0.0: 487 | version "3.1.0" 488 | resolved "https://registry.npmmirror.com/jsonc-parser/-/jsonc-parser-3.1.0.tgz#73b8f0e5c940b83d03476bc2e51a20ef0932615d" 489 | integrity sha512-DRf0QjnNeCUds3xTjKlQQ3DpJD51GvDjJfnxUVWg6PZTo2otSm+slzNAxU/35hF8/oJIKoG9slq30JYOsF2azg== 490 | 491 | magic-string@^0.25.7: 492 | version "0.25.9" 493 | resolved "https://registry.npmmirror.com/magic-string/-/magic-string-0.25.9.tgz#de7f9faf91ef8a1c91d02c2e5314c8277dbcdd1c" 494 | integrity sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ== 495 | dependencies: 496 | sourcemap-codec "^1.4.8" 497 | 498 | markdown-it-task-checkbox@^1.0.6: 499 | version "1.0.6" 500 | resolved "https://registry.npmmirror.com/markdown-it-task-checkbox/-/markdown-it-task-checkbox-1.0.6.tgz#9ebd7b6382e99162264605bc580f2ac118be4242" 501 | integrity sha512-7pxkHuvqTOu3iwVGmDPeYjQg+AIS9VQxzyLP9JCg9lBjgPAJXGEkChK6A2iFuj3tS0GV3HG2u5AMNhcQqwxpJw== 502 | 503 | nanoid@^3.3.4: 504 | version "3.3.4" 505 | resolved "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab" 506 | integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw== 507 | 508 | path-parse@^1.0.7: 509 | version "1.0.7" 510 | resolved "https://registry.npmmirror.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" 511 | integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== 512 | 513 | picocolors@^1.0.0: 514 | version "1.0.0" 515 | resolved "https://registry.npmmirror.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" 516 | integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== 517 | 518 | postcss@^8.1.10, postcss@^8.4.13: 519 | version "8.4.14" 520 | resolved "https://registry.npmmirror.com/postcss/-/postcss-8.4.14.tgz#ee9274d5622b4858c1007a74d76e42e56fd21caf" 521 | integrity sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig== 522 | dependencies: 523 | nanoid "^3.3.4" 524 | picocolors "^1.0.0" 525 | source-map-js "^1.0.2" 526 | 527 | preact@^10.0.0: 528 | version "10.10.0" 529 | resolved "https://registry.npmmirror.com/preact/-/preact-10.10.0.tgz#7434750a24b59dae1957d95dc0aa47a4a8e9a180" 530 | integrity sha512-fszkg1iJJjq68I4lI8ZsmBiaoQiQHbxf1lNq+72EmC/mZOsFF5zn3k1yv9QGoFgIXzgsdSKtYymLJsrJPoamjQ== 531 | 532 | resolve@^1.22.0: 533 | version "1.22.1" 534 | resolved "https://registry.npmmirror.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" 535 | integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== 536 | dependencies: 537 | is-core-module "^2.9.0" 538 | path-parse "^1.0.7" 539 | supports-preserve-symlinks-flag "^1.0.0" 540 | 541 | rollup@^2.59.0: 542 | version "2.77.2" 543 | resolved "https://registry.npmmirror.com/rollup/-/rollup-2.77.2.tgz#6b6075c55f9cc2040a5912e6e062151e42e2c4e3" 544 | integrity sha512-m/4YzYgLcpMQbxX3NmAqDvwLATZzxt8bIegO78FZLl+lAgKJBd1DRAOeEiZcKOIOPjxE6ewHWHNgGEalFXuz1g== 545 | optionalDependencies: 546 | fsevents "~2.3.2" 547 | 548 | shiki@^0.10.1: 549 | version "0.10.1" 550 | resolved "https://registry.npmmirror.com/shiki/-/shiki-0.10.1.tgz#6f9a16205a823b56c072d0f1a0bcd0f2646bef14" 551 | integrity sha512-VsY7QJVzU51j5o1+DguUd+6vmCmZ5v/6gYu4vyYAhzjuNQU6P/vmSy4uQaOhvje031qQMiW0d2BwgMH52vqMng== 552 | dependencies: 553 | jsonc-parser "^3.0.0" 554 | vscode-oniguruma "^1.6.1" 555 | vscode-textmate "5.2.0" 556 | 557 | source-map-js@^1.0.2: 558 | version "1.0.2" 559 | resolved "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" 560 | integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== 561 | 562 | source-map@^0.6.1: 563 | version "0.6.1" 564 | resolved "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" 565 | integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== 566 | 567 | sourcemap-codec@^1.4.8: 568 | version "1.4.8" 569 | resolved "https://registry.npmmirror.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4" 570 | integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA== 571 | 572 | supports-preserve-symlinks-flag@^1.0.0: 573 | version "1.0.0" 574 | resolved "https://registry.npmmirror.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" 575 | integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== 576 | 577 | vite@^2.9.7: 578 | version "2.9.14" 579 | resolved "https://registry.npmmirror.com/vite/-/vite-2.9.14.tgz#c438324c6594afd1050df3777da981dee988bb1b" 580 | integrity sha512-P/UCjSpSMcE54r4mPak55hWAZPlyfS369svib/gpmz8/01L822lMPOJ/RYW6tLCe1RPvMvOsJ17erf55bKp4Hw== 581 | dependencies: 582 | esbuild "^0.14.27" 583 | postcss "^8.4.13" 584 | resolve "^1.22.0" 585 | rollup "^2.59.0" 586 | optionalDependencies: 587 | fsevents "~2.3.2" 588 | 589 | vitepress@^1.0.0-alpha.4: 590 | version "1.0.0-alpha.4" 591 | resolved "https://registry.npmmirror.com/vitepress/-/vitepress-1.0.0-alpha.4.tgz#2d9929e2cade3d98f57f61848c01968fb386cee0" 592 | integrity sha512-bOAA4KW6vYGlkbcrPLZLTKWTgXVroObU+o9xj9EENyEl6yg26WWvfN7DGA4BftjdM5O8nR93Z5khPQ3W/tFE7Q== 593 | dependencies: 594 | "@docsearch/css" "^3.0.0" 595 | "@docsearch/js" "^3.0.0" 596 | "@vitejs/plugin-vue" "^2.3.2" 597 | "@vue/devtools-api" "^6.1.4" 598 | "@vueuse/core" "^8.5.0" 599 | body-scroll-lock "^4.0.0-beta.0" 600 | shiki "^0.10.1" 601 | vite "^2.9.7" 602 | vue "^3.2.33" 603 | 604 | vscode-oniguruma@^1.6.1: 605 | version "1.6.2" 606 | resolved "https://registry.npmmirror.com/vscode-oniguruma/-/vscode-oniguruma-1.6.2.tgz#aeb9771a2f1dbfc9083c8a7fdd9cccaa3f386607" 607 | integrity sha512-KH8+KKov5eS/9WhofZR8M8dMHWN2gTxjMsG4jd04YhpbPR91fUj7rYQ2/XjeHCJWbg7X++ApRIU9NUwM2vTvLA== 608 | 609 | vscode-textmate@5.2.0: 610 | version "5.2.0" 611 | resolved "https://registry.npmmirror.com/vscode-textmate/-/vscode-textmate-5.2.0.tgz#01f01760a391e8222fe4f33fbccbd1ad71aed74e" 612 | integrity sha512-Uw5ooOQxRASHgu6C7GVvUxisKXfSgW4oFlO+aa+PAkgmH89O3CXxEEzNRNtHSqtXFTl0nAC1uYj0GMSH27uwtQ== 613 | 614 | vue-demi@*: 615 | version "0.13.6" 616 | resolved "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.13.6.tgz#f9433cbd75e68a970dec066647f4ba6c08ced48f" 617 | integrity sha512-02NYpxgyGE2kKGegRPYlNQSL1UWfA/+JqvzhGCOYjhfbLWXU5QQX0+9pAm/R2sCOPKr5NBxVIab7fvFU0B1RxQ== 618 | 619 | vue@^3.2.33, vue@^3.2.37: 620 | version "3.2.37" 621 | resolved "https://registry.npmmirror.com/vue/-/vue-3.2.37.tgz#da220ccb618d78579d25b06c7c21498ca4e5452e" 622 | integrity sha512-bOKEZxrm8Eh+fveCqS1/NkG/n6aMidsI6hahas7pa0w/l7jkbssJVsRhVDs07IdDq7h9KHswZOgItnwJAgtVtQ== 623 | dependencies: 624 | "@vue/compiler-dom" "3.2.37" 625 | "@vue/compiler-sfc" "3.2.37" 626 | "@vue/runtime-dom" "3.2.37" 627 | "@vue/server-renderer" "3.2.37" 628 | "@vue/shared" "3.2.37" 629 | --------------------------------------------------------------------------------