From ac851d1b0d85c6622a259a3c0393343fc2cd474a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 22 May 2025 20:36:01 +0000 Subject: [PATCH 1/4] Initial plan for issue From 0e796a6362dafbf596acbb4cc854c63b1d81bfaa Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 22 May 2025 20:44:55 +0000 Subject: [PATCH 2/4] Initial plan for fixing transitive sqlproj dependencies issue Co-authored-by: zijchen <13544267+zijchen@users.noreply.github.com> --- test/Microsoft.Build.Sql.Tests/Microsoft.Build.Sql.Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Microsoft.Build.Sql.Tests/Microsoft.Build.Sql.Tests.csproj b/test/Microsoft.Build.Sql.Tests/Microsoft.Build.Sql.Tests.csproj index 75dbe86..0fcbfdd 100644 --- a/test/Microsoft.Build.Sql.Tests/Microsoft.Build.Sql.Tests.csproj +++ b/test/Microsoft.Build.Sql.Tests/Microsoft.Build.Sql.Tests.csproj @@ -3,7 +3,7 @@ - net8.0;net9.0;net10.0 + net8.0 enable false From e2a23ebf89cab947861c99bf82c1cb71265dd3d2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 22 May 2025 20:48:20 +0000 Subject: [PATCH 3/4] Add test for verifying transitive project references with DatabaseSqlCmdVariable Co-authored-by: zijchen <13544267+zijchen@users.noreply.github.com> --- test/Microsoft.Build.Sql.Tests/BuildTests.cs | 42 +++++++++++++++++++ .../Microsoft.Build.Sql.Tests/ProjectUtils.cs | 28 +++++++++++++ 2 files changed, 70 insertions(+) diff --git a/test/Microsoft.Build.Sql.Tests/BuildTests.cs b/test/Microsoft.Build.Sql.Tests/BuildTests.cs index 7e2078f..d01906d 100644 --- a/test/Microsoft.Build.Sql.Tests/BuildTests.cs +++ b/test/Microsoft.Build.Sql.Tests/BuildTests.cs @@ -505,5 +505,47 @@ public void VerifyBuildTargetPath() Assert.AreEqual(string.Empty, stdError); StringAssert.Contains($"Target path from PrintTargetPath: {GetDacpacPath()}", stdOutput, "Target path not found in output."); } + + [Test] + // https://github.com/microsoft/DacFx/issues/608 + [Description("Verifies build with transitive project references with DatabaseSqlCmdVariable should not have name collisions.")] + public void VerifyBuildWithTransitiveProjectReferencesWithDatabaseVariables() + { + // Create 3 sqlproj projects like Sql1, Sql2, Sql3 + string projectSql1 = Path.Combine(WorkingDirectory, "Sql1", "Sql1.sqlproj"); + string projectSql2 = Path.Combine(WorkingDirectory, "Sql2", "Sql2.sqlproj"); + string projectSql3 = Path.Combine(WorkingDirectory, "Sql3", "Sql3.sqlproj"); + + // Create identical Table1.sql in all three projects + string table1Sql = "CREATE TABLE Table1 (Id INT);\nGO\n"; + Directory.CreateDirectory(Path.Combine(WorkingDirectory, "Sql1")); + Directory.CreateDirectory(Path.Combine(WorkingDirectory, "Sql2")); + Directory.CreateDirectory(Path.Combine(WorkingDirectory, "Sql3")); + + File.WriteAllText(Path.Combine(WorkingDirectory, "Sql1", "Table1.sql"), table1Sql); + File.WriteAllText(Path.Combine(WorkingDirectory, "Sql2", "Table1.sql"), table1Sql); + File.WriteAllText(Path.Combine(WorkingDirectory, "Sql3", "Table1.sql"), table1Sql); + + // Create the sqlproj files + TestUtils.CreateProjectFile(projectSql1); + TestUtils.CreateProjectFile(projectSql2); + TestUtils.CreateProjectFile(projectSql3); + + // Add references with DatabaseSqlCmdVariable + // Sql2 references Sql1 with DatabaseSqlCmdVariable="Sql1" + ProjectUtils.AddItemGroup(projectSql2, "ProjectReference", new string[] { projectSql1 }); + ProjectUtils.SetItemMetadata(projectSql2, "ProjectReference", projectSql1, "DatabaseSqlCmdVariable", "Sql1"); + + // Sql3 references Sql2 with DatabaseSqlCmdVariable="Sql2" + ProjectUtils.AddItemGroup(projectSql3, "ProjectReference", new string[] { projectSql2 }); + ProjectUtils.SetItemMetadata(projectSql3, "ProjectReference", projectSql2, "DatabaseSqlCmdVariable", "Sql2"); + + // Build Sql3 project + int exitCode = this.RunGenericDotnetCommand($"build {projectSql3}", out string stdOutput, out string stdError); + + // Build should succeed - different databases should not have name collisions + Assert.AreEqual(0, exitCode, "Build failed with error " + stdError); + Assert.AreEqual(string.Empty, stdError); + } } } \ No newline at end of file diff --git a/test/Microsoft.Build.Sql.Tests/ProjectUtils.cs b/test/Microsoft.Build.Sql.Tests/ProjectUtils.cs index 62a7c17..4b4d08e 100644 --- a/test/Microsoft.Build.Sql.Tests/ProjectUtils.cs +++ b/test/Microsoft.Build.Sql.Tests/ProjectUtils.cs @@ -94,6 +94,34 @@ public static void AddItemRemoveGroup(string projectFilePath, string itemName, s } } + /// + /// Sets metadata on an item in a project. + /// + /// Path to the project file. + /// Name of the item type (e.g., "ProjectReference"). + /// Include path of the item to set metadata on. + /// Name of the metadata to set. + /// Value of the metadata to set. + public static void SetItemMetadata(string projectFilePath, string itemName, string itemInclude, string metadataName, string metadataValue) + { + using (ProjectCollection projectCollection = GetNewEngine()) + { + Project project = new Project(projectFilePath, null, "Current", projectCollection, ProjectLoadSettings.IgnoreMissingImports); + + foreach (ProjectItemElement item in project.Xml.Items) + { + if (item.ItemType == itemName && item.Include == itemInclude) + { + item.SetMetadataValue(metadataName, metadataValue); + break; + } + } + + project.Save(project.FullPath); + projectCollection.UnloadAllProjects(); + } + } + public static void AddTarget(string projectFilePath, string targetName, Action targetAction) { using (ProjectCollection projectCollection = GetNewEngine()) From a4097c52d240c88ee41c0030bc506c970f63d4fc Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 22 May 2025 20:59:59 +0000 Subject: [PATCH 4/4] Fix issue with transitive project references and preserve DatabaseSqlCmdVariable metadata Co-authored-by: zijchen <13544267+zijchen@users.noreply.github.com> --- src/Microsoft.Build.Sql/sdk/Sdk.targets | 16 +++++++ test/Microsoft.Build.Sql.Tests/BuildTests.cs | 47 +++++++++++-------- .../Microsoft.Build.Sql.Tests/ProjectUtils.cs | 28 ----------- 3 files changed, 44 insertions(+), 47 deletions(-) diff --git a/src/Microsoft.Build.Sql/sdk/Sdk.targets b/src/Microsoft.Build.Sql/sdk/Sdk.targets index f23baff..f04531c 100644 --- a/src/Microsoft.Build.Sql/sdk/Sdk.targets +++ b/src/Microsoft.Build.Sql/sdk/Sdk.targets @@ -184,6 +184,22 @@ + + + + + + + %(ProjectReferenceWithConfiguration.DatabaseSqlCmdVariable) + + + %(ProjectReferenceWithConfiguration.ServerSqlCmdVariable) + + + %(ProjectReferenceWithConfiguration.DatabaseVariableLiteralValue) + + + diff --git a/test/Microsoft.Build.Sql.Tests/BuildTests.cs b/test/Microsoft.Build.Sql.Tests/BuildTests.cs index d01906d..b07e9ec 100644 --- a/test/Microsoft.Build.Sql.Tests/BuildTests.cs +++ b/test/Microsoft.Build.Sql.Tests/BuildTests.cs @@ -512,33 +512,42 @@ public void VerifyBuildTargetPath() public void VerifyBuildWithTransitiveProjectReferencesWithDatabaseVariables() { // Create 3 sqlproj projects like Sql1, Sql2, Sql3 - string projectSql1 = Path.Combine(WorkingDirectory, "Sql1", "Sql1.sqlproj"); - string projectSql2 = Path.Combine(WorkingDirectory, "Sql2", "Sql2.sqlproj"); - string projectSql3 = Path.Combine(WorkingDirectory, "Sql3", "Sql3.sqlproj"); + string projectSql1Dir = Path.Combine(WorkingDirectory, "Sql1"); + string projectSql2Dir = Path.Combine(WorkingDirectory, "Sql2"); + string projectSql3Dir = Path.Combine(WorkingDirectory, "Sql3"); + string projectSql1 = Path.Combine(projectSql1Dir, "Sql1.sqlproj"); + string projectSql2 = Path.Combine(projectSql2Dir, "Sql2.sqlproj"); + string projectSql3 = Path.Combine(projectSql3Dir, "Sql3.sqlproj"); // Create identical Table1.sql in all three projects string table1Sql = "CREATE TABLE Table1 (Id INT);\nGO\n"; - Directory.CreateDirectory(Path.Combine(WorkingDirectory, "Sql1")); - Directory.CreateDirectory(Path.Combine(WorkingDirectory, "Sql2")); - Directory.CreateDirectory(Path.Combine(WorkingDirectory, "Sql3")); - - File.WriteAllText(Path.Combine(WorkingDirectory, "Sql1", "Table1.sql"), table1Sql); - File.WriteAllText(Path.Combine(WorkingDirectory, "Sql2", "Table1.sql"), table1Sql); - File.WriteAllText(Path.Combine(WorkingDirectory, "Sql3", "Table1.sql"), table1Sql); - - // Create the sqlproj files - TestUtils.CreateProjectFile(projectSql1); - TestUtils.CreateProjectFile(projectSql2); - TestUtils.CreateProjectFile(projectSql3); + Directory.CreateDirectory(projectSql1Dir); + Directory.CreateDirectory(projectSql2Dir); + Directory.CreateDirectory(projectSql3Dir); + + File.WriteAllText(Path.Combine(projectSql1Dir, "Table1.sql"), table1Sql); + File.WriteAllText(Path.Combine(projectSql2Dir, "Table1.sql"), table1Sql); + File.WriteAllText(Path.Combine(projectSql3Dir, "Table1.sql"), table1Sql); + + // Copy from template to create project files + File.Copy( + Path.Combine(TestContext.CurrentContext.TestDirectory, "Template", "project.sqlproj"), + projectSql1); + File.Copy( + Path.Combine(TestContext.CurrentContext.TestDirectory, "Template", "project.sqlproj"), + projectSql2); + File.Copy( + Path.Combine(TestContext.CurrentContext.TestDirectory, "Template", "project.sqlproj"), + projectSql3); // Add references with DatabaseSqlCmdVariable // Sql2 references Sql1 with DatabaseSqlCmdVariable="Sql1" - ProjectUtils.AddItemGroup(projectSql2, "ProjectReference", new string[] { projectSql1 }); - ProjectUtils.SetItemMetadata(projectSql2, "ProjectReference", projectSql1, "DatabaseSqlCmdVariable", "Sql1"); + ProjectUtils.AddItemGroup(projectSql2, "ProjectReference", new string[] { projectSql1 }, + item => item.AddMetadata("DatabaseSqlCmdVariable", "Sql1")); // Sql3 references Sql2 with DatabaseSqlCmdVariable="Sql2" - ProjectUtils.AddItemGroup(projectSql3, "ProjectReference", new string[] { projectSql2 }); - ProjectUtils.SetItemMetadata(projectSql3, "ProjectReference", projectSql2, "DatabaseSqlCmdVariable", "Sql2"); + ProjectUtils.AddItemGroup(projectSql3, "ProjectReference", new string[] { projectSql2 }, + item => item.AddMetadata("DatabaseSqlCmdVariable", "Sql2")); // Build Sql3 project int exitCode = this.RunGenericDotnetCommand($"build {projectSql3}", out string stdOutput, out string stdError); diff --git a/test/Microsoft.Build.Sql.Tests/ProjectUtils.cs b/test/Microsoft.Build.Sql.Tests/ProjectUtils.cs index 4b4d08e..62a7c17 100644 --- a/test/Microsoft.Build.Sql.Tests/ProjectUtils.cs +++ b/test/Microsoft.Build.Sql.Tests/ProjectUtils.cs @@ -94,34 +94,6 @@ public static void AddItemRemoveGroup(string projectFilePath, string itemName, s } } - /// - /// Sets metadata on an item in a project. - /// - /// Path to the project file. - /// Name of the item type (e.g., "ProjectReference"). - /// Include path of the item to set metadata on. - /// Name of the metadata to set. - /// Value of the metadata to set. - public static void SetItemMetadata(string projectFilePath, string itemName, string itemInclude, string metadataName, string metadataValue) - { - using (ProjectCollection projectCollection = GetNewEngine()) - { - Project project = new Project(projectFilePath, null, "Current", projectCollection, ProjectLoadSettings.IgnoreMissingImports); - - foreach (ProjectItemElement item in project.Xml.Items) - { - if (item.ItemType == itemName && item.Include == itemInclude) - { - item.SetMetadataValue(metadataName, metadataValue); - break; - } - } - - project.Save(project.FullPath); - projectCollection.UnloadAllProjects(); - } - } - public static void AddTarget(string projectFilePath, string targetName, Action targetAction) { using (ProjectCollection projectCollection = GetNewEngine())