C++版clover支持安装net3.5,仅限于本地,因为下载站不会放那么大的包
大石头 authored at 2025-07-07 20:34:42
9.59 KiB
LuckyClover
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;

namespace LuckyClover;

#if NET20
delegate void Action();
#endif

internal class Program
{
    private static readonly Dictionary<String, Action> _menus = new(StringComparer.OrdinalIgnoreCase);
    private const String _baseUrl = "http://x.newlifex.com/dotnet";

    private static void Main(String[] args)
    {
        //XTrace.UseConsole();

        var cmd = "";
        // 命令取第一个非选项、非URL参数,避免把 -silent/-force/-dryrun 或下载地址当成命令
        for (var i = 0; i < args.Length; i++)
        {
            if (args[i].StartsWith("-") || args[i].StartsWith("http", StringComparison.InvariantCultureIgnoreCase)) continue;

            cmd = args[i];
            break;
        }

        if (cmd != "zip" && cmd != "unzip" && cmd != "tar" && cmd != "untar")
        {
            var asm = Assembly.GetEntryAssembly();
            var ver = asm.GetName().Version + "";
            var fver = ver;
            var vers = asm.GetCustomAttributes(typeof(AssemblyFileVersionAttribute), false);
            if (vers != null && vers.Length > 0)
            {
                var vatt = vers[0] as AssemblyFileVersionAttribute;
                if (!String.IsNullOrEmpty(vatt.Version)) fver = vatt.Version;
            }

            Log.WriteLine("幸运草 \e[31;1mLuckyClover\e[0m v{0}", fver);
            //Console.WriteLine("无依赖编译为linux-arm/linux-x86/windows,用于自动安装主流.NET运行时");
#if NETFRAMEWORK
            var atts = asm.GetCustomAttributes(typeof(AssemblyDescriptionAttribute), false);
            Console.WriteLine((atts[0] as AssemblyDescriptionAttribute).Description);
#else
            Console.WriteLine(asm.GetCustomAttribute<AssemblyDescriptionAttribute>()?.Description);
#endif
            Console.WriteLine("{0}", Environment.OSVersion);
            Console.WriteLine("架构:系统 {0} 位,进程 {1} 位", NetRuntime.OSBits, NetRuntime.ProcessBits);
            Console.WriteLine("运行时:\e[34;1m{0}\e[0m", Environment.Version);
            Console.WriteLine("发布:{0:yyyy-MM-dd HH:mm:ss}", GetCompileTime(ver));
            Console.WriteLine();
        }

        var net = new NetRuntime
        {
            BaseUrl = _baseUrl,
            Hashs = NetRuntime.LoadMD5s(),
        };

        // 读取命令行,一般放在最后
        var flag = false;
        for (var i = 0; i < args.Length; i++)
        {
            if (args[i].StartsWith("http"))
            {
                var url = args[i];
                if (url[url.Length - 1] == '/') url = url.Substring(0, url.Length - 1);
                net.BaseUrl = url;
                flag = true;

                break;
            }
        }
        // 读取本目录 server.txt
        if (!flag)
        {
            var f = Path.GetFullPath("server.txt");
            if (File.Exists(f))
            {
                var url = File.ReadAllText(f).Trim();
                if (!String.IsNullOrEmpty(url))
                {
                    if (url[url.Length - 1] == '/') url = url.Substring(0, url.Length - 1);
                    if (!url.EndsWith("/dotnet", true, null)) url += "/dotnet";
                    net.BaseUrl = url;
                    flag = true;
                }
            }
        }
        // 读取静默安装标记
        for (var i = 0; i < args.Length; i++)
        {
            if (args[i].Equals("-silent", StringComparison.InvariantCultureIgnoreCase))
            {
                net.Silent = true;

                break;
            }
        }
        // 读取强制安装标记
        foreach (var item in args)
        {
            if (item.Equals("-force", StringComparison.InvariantCultureIgnoreCase)) net.Force = true;
            if (item.Equals("-dryrun", StringComparison.InvariantCultureIgnoreCase)) net.DryRun = true;
        }

        // 根据操作系统,自动选择安装NET版本
        _menus["net"] = net.AutoInstallNet;
        _menus["auto"] = net.AutoInstallNet;

        if (NetRuntime.IsWindows)
        {
            _menus["net20"] = () => net.InstallNet20();
            _menus["net40"] = () => net.InstallNet40();
            _menus["net45"] = () => net.InstallNet45();
            _menus["net48"] = () => net.InstallNet48();
        }

        var v6 = NetRuntime.Version6;
        var v8 = NetRuntime.Version8;
        var v10 = NetRuntime.Version10;
        var v11 = NetRuntime.Version11;
        _menus["net6"] = () => net.InstallNet("v6.0", v6, null);
        _menus["net6-desktop"] = () => net.InstallNet("v6.0", v6, "desktop");
        _menus["net6-aspnet"] = () => net.InstallNet("v6.0", v6, "aspnet");
        _menus["net6-host"] = () => net.InstallNet("v6.0", v6, "host");
        _menus["net8"] = () => net.InstallNet("v8.0", v8, null);
        _menus["net8-desktop"] = () => net.InstallNet("v8.0", v8, "desktop");
        _menus["net8-aspnet"] = () => net.InstallNet("v8.0", v8, "aspnet");
        _menus["net8-host"] = () => net.InstallNet("v8.0", v8, "host");
        _menus["net10"] = () => net.InstallNet("v10.0", v10, null);
        _menus["net10-desktop"] = () => net.InstallNet("v10.0", v10, "desktop");
        _menus["net10-aspnet"] = () => net.InstallNet("v10.0", v10, "aspnet");
        _menus["net10-host"] = () => net.InstallNet("v10.0", v10, "host");
        _menus["net11"] = () => net.InstallNet("v11.0", v11, null);
        _menus["net11-desktop"] = () => net.InstallNet("v11.0", v11, "desktop");
        _menus["net11-aspnet"] = () => net.InstallNet("v11.0", v11, "aspnet");
        _menus["net11-host"] = () => net.InstallNet("v11.0", v11, "host");

        _menus["md5"] = () => ShowMd5(args);
#if NET45_OR_GREATER || NETCOREAPP
        _menus["zip"] = () => new ZipCommand().Compress(args);
        _menus["unzip"] = () => new ZipCommand().Extract(args);
#endif
#if NET7_0_OR_GREATER
        _menus["tar"] = () => new TarCommand().Compress(args);
        _menus["untar"] = () => new TarCommand().Extract(args);
#endif

        if (String.IsNullOrEmpty(cmd)) cmd = ShowMenu() ?? "";

        // 无参数且没有交互输入时 cmd 为空,直接给帮助,避免用 null 去查菜单字典
        if (String.IsNullOrEmpty(cmd))
        {
            PrintUsage();

            return;
        }

        if (_menus.TryGetValue(cmd, out var func))
            func();
        else
            Console.WriteLine("无法识别命令:{0}", cmd);
    }

    private static void PrintUsage()
    {
        Console.WriteLine("用法:clover <命令> [选项]");
        Console.WriteLine("  net20|net40|net45|net48|net6|net8|net10|net11   安装指定运行时");
        Console.WriteLine("  net6-desktop|net8-desktop|net10-desktop         安装桌面运行时");
        Console.WriteLine("  net6-aspnet|net8-aspnet|net10-aspnet            安装 ASP.NET Core 运行时");
        Console.WriteLine("  auto|net                                        按操作系统自动选择");
        Console.WriteLine("  md5 [pattern]                                   列出文件校验值");
        Console.WriteLine("  zip|unzip|tar|untar                             压缩解压");
        Console.WriteLine("选项:-silent 静默安装;-force 强制安装;-dryrun 只打印计划;<url> 指定下载服务器");
    }

    private static String ShowMenu()
    {
        var vers = new List<VerInfo>();
        if (NetRuntime.IsWindows)
        {
            vers.AddRange(NetRuntime.Get1To45VersionFromRegistry());
            vers.AddRange(NetRuntime.Get45PlusFromRegistry());
        }
        vers.AddRange(NetRuntime.GetNetCore());

        Console.WriteLine("已安装版本:");
        foreach (var item in vers)
        {
            if (String.IsNullOrEmpty(item.Sp))
                Console.WriteLine("{0,-10}\t{1}", item.Name, item.Version);
            else
                Console.WriteLine("{0,-10}\t{1} Sp{2}", item.Name, item.Version, item.Sp);
        }
        Console.WriteLine("");

        Console.WriteLine("命令:\e[31;1mclover net20|net40|net45|net48|net6|net8|net10|net11\e[0m");
        Console.WriteLine("");

        var ms = new String[_menus.Count];
        _menus.Keys.CopyTo(ms, 0);
        Console.WriteLine("可用命令:\e[34;1m{0}\e[0m", String.Join(", ", ms));

        var line = Console.ReadLine()?.Trim();

        return line;
    }

    private static void ShowMd5(String[] args)
    {
        var pt = "*.*";
        if (args != null && args.Length >= 2) pt = args[1];
        //Console.WriteLine("pt={0}", pt);

        var di = new DirectoryInfo("./");
        foreach (var fi in di.GetFiles(pt))
        {
            Console.WriteLine("{0}\t{1}", fi.Name, NetRuntime.GetMD5(fi.FullName));
        }
    }

    /// <summary>根据版本号计算得到编译时间</summary>
    /// <param name="version"></param>
    /// <returns></returns>
    public static DateTime GetCompileTime(String version)
    {
        var ss = version?.Split(['.']);
        if (ss == null || ss.Length < 4) return DateTime.MinValue;

        if (!Int32.TryParse(ss[2], out var d)) d = 0;
        if (!Int32.TryParse(ss[3], out var s)) s = 0;
        var y = DateTime.Today.Year;

        // 指定年月日的版本格式 1.0.yyyy.mmdd
        if (d <= y && d >= y - 10)
        {
            var dt = new DateTime(d, 1, 1);
            if (s > 0)
            {
                if (s >= 200) dt = dt.AddMonths(s / 100 - 1);
                s %= 100;
                if (s > 1) dt = dt.AddDays(s - 1);
            }

            return dt;
        }
        else
        {
            var dt = new DateTime(2000, 1, 1);
            dt = dt.AddDays(d).AddSeconds(s * 2);

            return dt;
        }
    }
}