c# 多项目编译发布

环境:net6.0

包:Nuke.Common

dotnet add package Nuke.Common --version 8.1.4

1、安装 Nuke.GlobalTool

dotnet tool install Nuke.GlobalTool --global

2、项目目录创建文件夹 .nuke

3、 .nuke文件夹下创建文件 build.schema.json ,内容如下

复制代码
{
  "$schema": "http://json-schema.org/draft-04/schema#",
  "title": "Build Schema",
  "properties": {
    "Solution": {
      "type": "string",
      "description": "Path to a solution file that is automatically loaded"
    }
  }
}

4、Build.cs 代码

cs 复制代码
    /// <summary>
    /// cmd指令
    /// dotnet run --project ProjectBuild.csproj -- CopyToTarget    # 完整重新构建打包
    /// dotnet run --project ProjectBuild.csproj -- Compile    # 只编译
    /// dotnet run --project ProjectBuild.csproj -- Publish    # 只发布
    /// dotnet run --project ProjectBuild.csproj -- CopyOnly   # 只复制
    /// </summary>
    class Build : NukeBuild
    {
        public static int Main() => Execute<Build>(x => x.CopyToTarget);

        [Parameter("Configuration to build - Default is 'Release'")]
        readonly string Configuration = "Release";

        /// <summary>
        /// 复制目标目录
        /// </summary>
        [Parameter("Target directory for copying files")]
        readonly string TargetDirectory = "C:\\Program Files\\PEER Group\\PTO 8.6 SP2\\PtoUpgrade";

        // 定义需要编译和发布的项目
        Dictionary<string, string> Projects => new Dictionary<string, string>
        {
            { "D:\\项目\\mc-api\\src\\AM.MC.WebApi\\AM.MC.WebApi.csproj", "mc-api" },
            { "D:\\项目\\mcm\\src\\AM.Mcm.WebApi\\AM.Mcm.WebApi.csproj", "mcm-api" },
            { "D:\\项目\\webui\\AM.MC.WebBlazorServer\\AM.MC.WebBlazorServer.csproj", "pe-web" }
        };

        // 排除文件规则(支持通配符)
        List<string> ExcludePatterns => new List<string>
        {
            // 排除文件
            "*.pdb",           // 调试符号文件
            "appsettings.Development.json",
            "appsettings.json",
            
            // 排除文件夹
            "logs/",           // 日志文件夹
            "temp/",           // 临时文件夹
            "RedisServer/",    
            
            // 排除特定文件
            "*.log",
            "*.tmp",
            ".gitignore",
            ".gitattributes"
        };

        // 输出目录
        AbsolutePath ArtifactsDirectory => RootDirectory / "artifacts";
        AbsolutePath PublishDirectory => ArtifactsDirectory / "publish";
        AbsolutePath CopyTargetDirectory => (AbsolutePath)TargetDirectory;

        Target CheckPaths => _ => _
            .Executes(() =>
            {
                Log.Information("验证项目文件路径...");

                var missingProjects = new List<string>();

                foreach (var project in Projects)
                {
                    if (!File.Exists(project.Key))
                    {
                        Log.Error($"✗ 找不到项目: {project.Key}");
                        missingProjects.Add(project.Key);
                    }
                    else
                    {
                        Log.Information($"✓ 找到: {project.Key}");
                    }
                }

                if (missingProjects.Any())
                {
                    throw new FileNotFoundException(
                        $"找不到 {missingProjects.Count} 个项目文件,请检查路径配置");
                }

                Log.Information($"发布目录: {PublishDirectory}");
                Log.Information($"目标目录: {CopyTargetDirectory}");
                Log.Information($"共找到 {Projects.Count} 个项目");
                Log.Information($"排除规则数: {ExcludePatterns.Count}");
            });

        Target Clean => _ => _
            .DependsOn(CheckPaths)
            .Before(Restore)
            .Executes(() =>
            {
                Log.Information("清理输出目录...");
                EnsureCleanDirectory(ArtifactsDirectory);
                EnsureCleanDirectory(PublishDirectory);
                Log.Information("清理完成");
            });

        Target Restore => _ => _
            .DependsOn(Clean)
            .Executes(() =>
            {
                Log.Information("开始恢复 NuGet 包...");

                int count = 0;
                foreach (var project in Projects)
                {
                    count++;
                    Log.Information($"[{count}/{Projects.Count}] 恢复: {project.Value}");
                    DotNetRestore(s => s.SetProjectFile(project.Key));
                }

                Log.Information("所有包恢复完成");
            });

        Target Compile => _ => _
            .DependsOn(Restore)
            .Executes(() =>
            {
                Log.Information("开始编译项目...");

                int count = 0;
                foreach (var project in Projects)
                {
                    count++;
                    Log.Information($"[{count}/{Projects.Count}] 编译: {project.Value} [{Configuration}]");

                    DotNetBuild(s => s
                        .SetProjectFile(project.Key)
                        .SetConfiguration(Configuration)
                        .EnableNoRestore());
                }

                Log.Information("所有项目编译完成");
            });

        Target Publish => _ => _
            .DependsOn(Compile)
            .Executes(() =>
            {
                Log.Information("开始发布项目...");

                int count = 0;
                foreach (var project in Projects)
                {
                    count++;
                    var publishPath = PublishDirectory / project.Value;

                    Log.Information($"[{count}/{Projects.Count}] 发布: {project.Value} -> {publishPath}");

                    DotNetPublish(s => s
                        .SetProject(project.Key)
                        .SetConfiguration(Configuration)
                        .SetOutput(publishPath)
                        .EnableNoRestore()
                        .DisableSelfContained()
                    );
                }

                Log.Information("所有项目发布完成");
            });

        Target CopyToTarget => _ => _
            .DependsOn(Publish)
            .Executes(() =>
            {
                Log.Information("开始复制文件到目标目录...");

                // 确保目标目录存在
                EnsureExistingDirectory(CopyTargetDirectory);

                var publishedProjects = Directory.GetDirectories(PublishDirectory);
                Log.Information($"找到 {publishedProjects.Length} 个发布目录");
                Log.Information($"目标根目录: {CopyTargetDirectory}");
                Log.Information("");

                int projectCount = 0;
                int totalCopiedFiles = 0;
                int totalExcludedFiles = 0;

                foreach (var projectDir in publishedProjects)
                {
                    projectCount++;
                    var projectName = Path.GetFileName(projectDir);
                    var targetProjectDir = CopyTargetDirectory / projectName;

                    Log.Information($"[{projectCount}/{publishedProjects.Length}] 处理项目: {projectName}");
                    Log.Information($"    源目录: {projectDir}");
                    Log.Information($"    目标目录: {targetProjectDir}");

                    // 清理目标目录(如果存在)
                    if (Directory.Exists(targetProjectDir))
                    {
                        Log.Information($"    清理已存在的目录...");
                        DeleteDirectory(targetProjectDir);
                    }

                    // 复制文件(应用排除规则)
                    var (copiedCount, excludedCount) = CopyDirectoryWithExclusions(
                        projectDir,
                        targetProjectDir,
                        ExcludePatterns);

                    totalCopiedFiles += copiedCount;
                    totalExcludedFiles += excludedCount;

                    Log.Information($"    ✓ 已复制 {copiedCount} 个文件,排除 {excludedCount} 个文件");
                    Log.Information("");
                }

                Log.Information("========================================");
                Log.Information("          复制完成统计");
                Log.Information("========================================");
                Log.Information($"  处理项目数: {projectCount}");
                Log.Information($"  复制文件数: {totalCopiedFiles}");
                Log.Information($"  排除文件数: {totalExcludedFiles}");
                Log.Information($"  目标目录: {CopyTargetDirectory}");
                Log.Information("========================================");
            });

        // 复制目录并应用排除规则
        private (int copiedCount, int excludedCount) CopyDirectoryWithExclusions(
            string sourceDir,
            string targetDir,
            List<string> excludePatterns)
        {
            int copiedCount = 0;
            int excludedCount = 0;

            // 创建目标目录
            Directory.CreateDirectory(targetDir);

            // 获取所有文件
            var allFiles = Directory.GetFiles(sourceDir, "*", SearchOption.AllDirectories);

            foreach (var sourceFile in allFiles)
            {
                // 获取相对路径
                var relativePath = Path.GetRelativePath(sourceDir, sourceFile);

                // 检查是否应该排除
                if (ShouldExclude(relativePath, excludePatterns))
                {
                    excludedCount++;
                    continue;
                }

                // 构建目标文件路径
                var targetFile = Path.Combine(targetDir, relativePath);
                var targetFileDir = Path.GetDirectoryName(targetFile);

                // 确保目标目录存在
                if (!Directory.Exists(targetFileDir))
                {
                    Directory.CreateDirectory(targetFileDir);
                }

                // 复制文件
                File.Copy(sourceFile, targetFile, overwrite: true);
                copiedCount++;
            }

            return (copiedCount, excludedCount);
        }

        // 检查文件或路径是否应该被排除
        private bool ShouldExclude(string relativePath, List<string> excludePatterns)
        {
            // 标准化路径分隔符
            relativePath = relativePath.Replace('\\', '/');

            foreach (var pattern in excludePatterns)
            {
                var normalizedPattern = pattern.Replace('\\', '/');

                // 处理文件夹排除(以 / 结尾)
                if (normalizedPattern.EndsWith("/"))
                {
                    var folderPattern = normalizedPattern.TrimEnd('/');
                    if (relativePath.StartsWith(folderPattern + "/", StringComparison.OrdinalIgnoreCase))
                    {
                        return true;
                    }
                }
                // 处理通配符匹配
                else if (normalizedPattern.Contains("*") || normalizedPattern.Contains("?"))
                {
                    var regexPattern = "^" + Regex.Escape(normalizedPattern)
                        .Replace("\\*", ".*")
                        .Replace("\\?", ".") + "$";

                    var fileName = Path.GetFileName(relativePath);
                    if (Regex.IsMatch(fileName, regexPattern, RegexOptions.IgnoreCase))
                    {
                        return true;
                    }
                }
                // 精确匹配
                else
                {
                    var fileName = Path.GetFileName(relativePath);
                    if (fileName.Equals(normalizedPattern, StringComparison.OrdinalIgnoreCase))
                    {
                        return true;
                    }
                }
            }

            return false;
        }

        // 额外目标:只复制(不重新编译)
        Target CopyOnly => _ => _
            .Executes(() =>
            {
                Log.Information("只复制已发布的文件(跳过编译)...");

                if (!Directory.Exists(PublishDirectory))
                {
                    throw new DirectoryNotFoundException(
                        $"发布目录不存在: {PublishDirectory}。请先运行 Publish 目标。");
                }

                EnsureExistingDirectory(CopyTargetDirectory);

                var publishedProjects = Directory.GetDirectories(PublishDirectory);
                if (publishedProjects.Length == 0)
                {
                    throw new InvalidOperationException(
                        $"发布目录中没有找到项目。请先运行 Publish 目标。");
                }

                Log.Information($"找到 {publishedProjects.Length} 个发布目录");

                int totalCopiedFiles = 0;
                int totalExcludedFiles = 0;

                foreach (var projectDir in publishedProjects)
                {
                    var projectName = Path.GetFileName(projectDir);
                    var targetProjectDir = CopyTargetDirectory / projectName;

                    Log.Information($"复制: {projectName}");

                    if (Directory.Exists(targetProjectDir))
                    {
                        DeleteDirectory(targetProjectDir);
                    }

                    var (copiedCount, excludedCount) = CopyDirectoryWithExclusions(
                        projectDir,
                        targetProjectDir,
                        ExcludePatterns);

                    totalCopiedFiles += copiedCount;
                    totalExcludedFiles += excludedCount;

                    Log.Information($"  ✓ 已复制 {copiedCount} 个文件,排除 {excludedCount} 个文件");
                }

                Log.Information($"✓ 复制完成:{totalCopiedFiles} 个文件已复制到 {CopyTargetDirectory}");
            });

        // 显示排除规则
        Target ShowExcludeRules => _ => _
            .Executes(() =>
            {
                Log.Information("========================================");
                Log.Information("          当前排除规则");
                Log.Information("========================================");

                var filePatterns = ExcludePatterns.Where(p => !p.EndsWith("/")).ToList();
                var folderPatterns = ExcludePatterns.Where(p => p.EndsWith("/")).ToList();

                Log.Information("");
                Log.Information("排除的文件模式:");
                foreach (var pattern in filePatterns)
                {
                    Log.Information($"  - {pattern}");
                }

                Log.Information("");
                Log.Information("排除的文件夹:");
                foreach (var pattern in folderPatterns)
                {
                    Log.Information($"  - {pattern.TrimEnd('/')}");
                }

                Log.Information("");
                Log.Information($"共 {ExcludePatterns.Count} 条排除规则");
                Log.Information("========================================");
            });
    }

指定csproj目录:

cs 复制代码
        Dictionary<string, string> Projects => new Dictionary<string, string>
        {
            { "D:\\项目\\mc-api\\src\\AM.MC.WebApi\\AM.MC.WebApi.csproj", "mc-api" },
            { "D:\\项目\\mcm\\src\\AM.Mcm.WebApi\\AM.Mcm.WebApi.csproj", "mcm-api" },
            { "D:\\项目\\webui\\AM.MC.WebBlazorServer\\AM.MC.WebBlazorServer.csproj", "pe-web" }
        };

4、代码

相关推荐
张人玉2 小时前
C#Https协议相关知识点
开发语言·https·c#
Byron Loong2 小时前
【C#】VS调试——带参调试
开发语言·c#
故事不长丨2 小时前
解锁C#编程秘籍:封装、继承、多态深度剖析
开发语言·数据库·c#
努力小周3 小时前
基于STM32物联网智能老年人防摔系统
stm32·单片机·嵌入式硬件·物联网·c#·课程设计
FuckPatience3 小时前
C# 补码
开发语言·算法·c#
烽火聊员4 小时前
SSLSocket 服务器端WPF C#测试代码
开发语言·c#·wpf·ssl
wangnaisheng4 小时前
【C#】RESTful的使用
c#
缺点内向4 小时前
如何在 C# 中高效的将 XML 转换为 PDF
xml·后端·pdf·c#·.net
时光追逐者4 小时前
Visual Studio 2026 正式版下载与安装详细教程!
ide·c#·.net·.net core·visual studio