Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Tasks]: 有关【开发时动态之热执行】相关功能的规划与任务细化 (意见搜集与讨论) #301

Open
20 of 25 tasks
NMSAzulX opened this issue Jun 3, 2024 · 8 comments
Labels
phase-done 任务或计划阶段性结束. tasks This is a tasklist.

Comments

@NMSAzulX
Copy link
Collaborator

NMSAzulX commented Jun 3, 2024

📃 计划清单 (Tasklist).

基于 VS C# 项目的热执行功能

@NMSAzulX NMSAzulX added the tasks This is a tasklist. label Jun 3, 2024
@NMSAzulX
Copy link
Collaborator Author

NMSAzulX commented Jun 3, 2024

热执行功能描述

  • 轻量重载

    • 在开发时,改动主项目代码,无需重新 F5,随时展示最新结果.
    • 支持 Release 模式热编译.
    • 与 EnC 无关.
  • 依赖重建

    • 在开发时,若依赖项目代码及依赖库改变, 无需重新 F5, 随时展示最新结果.
    • 支持 Release 模式热重建.
    • 与 EnC 无关.

@NMSAzulX NMSAzulX pinned this issue Jun 3, 2024
@NMSAzulX
Copy link
Collaborator Author

NMSAzulX commented Jun 3, 2024

不使用 SG 的通用写法

理论支持 std2.0/2.1. 实际受 DotNetCore.Natasha.CSharp.Compiler.Domain 包版本限制. 除非自实现 DotNetCore.Natasha.DynamicLoad.Base 包。

class Program
{

        public static void Main(string[] args)
        {

            //设置当前程序的类型 ,默认为 console
            HEProxy.SetProjectKind(HEProjectKind.Console);


            //创建热执行的日志输出实例
            HEFileLogger logger = new HEFileLogger(debugFilePath);
            //设置信息输出方式
            HEProxy.ShowMessage = async msg => {
                await logger.WriteUtf8FileAsync(msg);
            };


            //设置 Natasha 需要排除的 程序集
            HEProxy.ExcludeGlobalUsing("System.Windows.Controls");


            //HE:Debug (当前热编译环境指向为 Debug 编译,可以不写,默认为 Debug)


            //编译初始化选项,主要是 Natasha 的初始化操作.
            //Once (热编译时使用 Once  剔除该语句,不写也没关系)
            HEProxy.SetCompileInitAction(()=>{{
                NatashaManagement.RegistDomainCreator<NatashaDomainCreator>();
                NatashaManagement.Preheating((asmName, @namespace) =>

                            !string.IsNullOrWhiteSpace(@namespace) && 
                            (@namespace.StartsWith(""Microsoft.VisualBasic"") ||
                             HEProxy.IsExcluded(@namespace)),

                            true, 
                            true);
            }});
           

            //开始执行动态代理.
            //Once (热编译时使用 Once  剔除该语句,不写也没关系)
            HEProxy.Run();
  
            for (int i = 0; i < args.Length; i++)
            {
                 Console.WriteLine(args[i]);
            }
  
            //Once (这句 `//Once` 必须写,防止热编译时被 `Console.ReadKey()` 阻塞)
            Console.ReadKey();

        }

        //方法体中的参数操作对应 Main(string[] args) 中的 args,  
        //热执行时,Main 将接收到 "参数11",“参数2”,“参数23”
        //非必要,可以不写
        public static void ProxyMainArguments()
        {
            ProjectDynamicProxy.AppendArgs("参数11");
            ProjectDynamicProxy.AppendArgs("参数2");
            ProjectDynamicProxy.AppendArgs("参数23");
        }
}

SG 清爽版版本写法

.NET5.0 及以上版本适用, 案例中省略了 ProxyMainArguments 方法

class Program
{
        //Console
        public static void Main(string[] args)
        {

            //HE:Debug (当前热编译环境指向为 Debug 编译,可以不写,默认为 Debug)


            for (int i = 0; i < args.Length; i++)
            {

                //下面这句 DS 注释在热编译中起到了输出的作用,可以代替 Console.WriteLine(args[i]);
                //输出方式可以通过 HEProxy.ShowMessage = async msg => {} 进行设置.

                //DS args[i]
                Console.WriteLine(args[i]);

            }
            Console.ReadKey();
        }

        //Asp.net
        public static void Main(string[] args)
        {

            //不支持 AOT 方案
            var builder = WebApplication.CreateBuilder(args);

            builder.Services.AddCors();
            var app = builder.Build();
            app.UseStaticFiles();
            //.....等等其他设置

            //销毁 app 等一系列可销毁的对象。
            //方法1 :
            HEProxy.SetPreHotExecut(async () => { await app.DisposeAsync(); });

            //方法2 :
            app.AsyncToHotExecutor();
            await app.RunAsync();

            Console.ReadKey();
        }

        //Winform
        [STAThread]
        static void Main()
        {
        
            //Once
            ApplicationConfiguration.Initialize();

            Application.Run(new Form1()); 
            //Or 两种初始化均支持
            Application.Run(new ApplicationContext(new Form1()));
        }
}

注:winform 若不使用 SG 则需在 主逻辑中添加不少管理 form 生命周期的代码。

@NMSAzulX NMSAzulX added the phase-done 任务或计划阶段性结束. label Jun 9, 2024
@NMSAzulX
Copy link
Collaborator Author

NMSAzulX commented Jun 9, 2024

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant HEProxy
    participant HETreeMethodRewriter
    participant HETreeTriviaRewriter
    
    User->>HEProxy: Start Hot Compilation
    activate HEProxy
    
    HEProxy->>HETreeMethodRewriter: Register Method Plugins
    activate HETreeMethodRewriter
    HETreeMethodRewriter->>HEProxy: Plugins Registered
    deactivate HETreeMethodRewriter
    
    HEProxy->>HETreeTriviaRewriter: Register Trivia Plugins
    activate HETreeTriviaRewriter
    HETreeTriviaRewriter->>HEProxy: Plugins Registered
    deactivate HETreeTriviaRewriter
    
    HEProxy->>HEProxy: Rewrite Methods and Comments with Plugins
    
    User->>HEProxy: Complete Hot Compilation
    deactivate HEProxy
Loading

@NMSAzulX
Copy link
Collaborator Author

NMSAzulX commented Jun 11, 2024

状态机支持

在热执行环境中,热执行库本身为高权限操作库,Main 首次执行会将主逻辑交给 [热执行库] 进行[域代理运行],因此后续除非更改依赖库 源码、 csproj 文件触发重建。

举例:
假设我们需要统计重建次数,正常需要在 Program 类中增加一个全局计数的变量,private static int counter. 但是如果触发热执行, 那么整个程序包括 Program 也将在一个新的域中执行。所以每次热执行你的 program 都将是一份新的。
为此我增加了 HEProxyState 作为状态机的操作。

//设置
HEProxyState .SetValue(0);
//或
HEProxyState <int>.Value = 0//增加计数
HEProxyState <int>.Value+=1

HEProxyState <T> 为泛型方法,接收不同类型的值。

顶级语句支持

  • 顶级语句检测并代理到运行方法中.

SG 支持

  • 仅支持 .NET5.0 及以上版本.

  • 控制台,全程只需进行注释 //Once 即可.

  • Asp.net Core 传递被注销的对象即可.

隐式 using 支持

  • 在组件命名空间无冲突情况下运行正常.

  • 出现 VB 命名空间的干扰,引申出复杂运行环境下不可预知的 CS0104 问题,由本身的 Using 引用和默认 using 集覆盖二者结合解决。

  • 在复杂环境,HE 支持在每个文件中使用 //HE: CS0104 + 命名空间 来手动剔除当前文件发生冲突的 UsingCode.

注释命令

  • 模式切换

    • //HE:Release (写在 Main 方法中, 不写默认 Debug)
  • 执行模式(同/异步)

    • //HE:Asycn (写在 Main 方法中, 在有阻塞式方法体中需要指定,避免阻塞热执行方法,异步执行代理方法)
  • 代码剔除

    • //Once (全局 多次处理 移除)
  • CS0104 问题

    • //HE:CS0104 using1;using2... (全局 多次处理 累加)
  • 预处理表达式输出

    • //DS [exp] 在 Debug 模式下输出 exp 结果.
    • //RS [exp] 在 Release 模式下输出 exp 结果.

兼容特殊语法节点指令

  • 内部方法
  • 匿名方法

支持桌面应用程序热执行

  • Winform

@NMSAzulX
Copy link
Collaborator Author

NMSAzulX commented Jun 20, 2024

已测试项

  • //Once 注释 for 语句.
  • //Once 注释 Console.ReadKey() 表达式.
  • //HE:Release 动态切换到 Release 模式.
  • //HE:CS0104 Using 排除实验.
  • //HE:Async 阻塞测试
  • //DS 热编译环境 DEBUG 预处理指令输出语句.
  • //RS 热编译环境 RELEASE 预处理指令输出语句.
  • 标准控制台程序.
  • 顶级语句控制台程序.
  • 隐式 using 标准控制台.
  • 隐式 using 顶级语句控制台.
  • 标准 Asp.net Core.
  • 隐式 using Asp.net Core.
  • 代理期传参.
  • 代理期动态编译.
  • 代理期状态机.
  • 隐式 using 代理期间的 CS0104 问题.
  • WIinform Form 启动代理.
  • Winform ApplicationContext 启动代理.

@NMSAzulX
Copy link
Collaborator Author

NMSAzulX commented Jun 26, 2024

现存问题

  • ASP.NET CORE 内存无法被释放.
  • .NET5.0 以下代码侵入性大.
  • SG 为当前程序自动引入 Nuget 包.

@NMSAzulX
Copy link
Collaborator Author

VS2022 更新后 SG 功能有BUG,目前不敢推广。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
phase-done 任务或计划阶段性结束. tasks This is a tasklist.
Projects
None yet
Development

No branches or pull requests

1 participant