From 97478dfe217058a1e0186d74d9630cc4c0112100 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 1 Sep 2026 20:05:04 +0000 Subject: [PATCH] Fix pack crash on Compile Remove-only items (#4222) paket pack --symbols threw 'Compile entry is in unknown format' when a project's item had neither Include nor Update attributes (a valid MSBuild construct used to exclude files from compilation). GetCompileItems now skips such entries instead of throwing. Closes #4222 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../PaketConfigFiles/ProjectFile.fs | 53 +++++++++---------- tests/Paket.Tests/Paket.Tests.fsproj | 1 + tests/Paket.Tests/ProjectFile/OutputSpecs.fs | 9 ++++ .../TestData/CompileRemoveOnly.csprojtest | 11 ++++ 4 files changed, 47 insertions(+), 27 deletions(-) create mode 100644 tests/Paket.Tests/ProjectFile/TestData/CompileRemoveOnly.csprojtest diff --git a/src/Paket.Core/PaketConfigFiles/ProjectFile.fs b/src/Paket.Core/PaketConfigFiles/ProjectFile.fs index c7fac7430c..8cac0b5a6e 100644 --- a/src/Paket.Core/PaketConfigFiles/ProjectFile.fs +++ b/src/Paket.Core/PaketConfigFiles/ProjectFile.fs @@ -2039,32 +2039,31 @@ type ProjectFile with let getCompileItem (projectFile, compileNode) = let projectFolder = projectFile.FileName |> Path.GetFullPath |> Path.GetDirectoryName - let sourceFile = - let file = - match compileNode |> getAttribute "Include" with - | Some file -> file - | None -> - match compileNode |> getAttribute "Update" with - | Some file -> file - | None -> failwithf "The Compile entry is in unknown format and doesn't contain a Update or Include attribute." - - file - |> normalizePath - |> fun relPath -> Path.Combine(projectFolder, relPath) - - let destPath = - compileNode - |> getDescendants "Link" - |> function - | [] -> createRelativePath (projectFolder + string Path.DirectorySeparatorChar) sourceFile - | linkNode :: _ -> linkNode.InnerText - |> normalizePath - |> Path.GetDirectoryName - { - SourceFile = sourceFile - DestinationPath = destPath - BaseDir = projectFolder - } + match compileNode |> getAttribute "Include" |> Option.orElseWith (fun () -> compileNode |> getAttribute "Update") with + | None -> + // Entries such as without an Include or Update attribute + // don't reference a source file, so they can't produce a compile item. Skip them + // instead of failing the whole pack/build (see GitHub issue #4222). + None + | Some file -> + let sourceFile = + file + |> normalizePath + |> fun relPath -> Path.Combine(projectFolder, relPath) + + let destPath = + compileNode + |> getDescendants "Link" + |> function + | [] -> createRelativePath (projectFolder + string Path.DirectorySeparatorChar) sourceFile + | linkNode :: _ -> linkNode.InnerText + |> normalizePath + |> Path.GetDirectoryName + Some { + SourceFile = sourceFile + DestinationPath = destPath + BaseDir = projectFolder + } let getRealItems compileItem = let sourceFolder = Path.GetDirectoryName(compileItem.SourceFile) @@ -2080,7 +2079,7 @@ type ProjectFile with this.GetProjects includeReferencedProjects cache |> this.ProjectsWithoutTemplates |> Seq.collect getCompileRefs - |> Seq.map getCompileItem + |> Seq.choose getCompileItem |> Seq.collect getRealItems diff --git a/tests/Paket.Tests/Paket.Tests.fsproj b/tests/Paket.Tests/Paket.Tests.fsproj index 832e4e2a91..a2dd610165 100644 --- a/tests/Paket.Tests/Paket.Tests.fsproj +++ b/tests/Paket.Tests/Paket.Tests.fsproj @@ -130,6 +130,7 @@ + diff --git a/tests/Paket.Tests/ProjectFile/OutputSpecs.fs b/tests/Paket.Tests/ProjectFile/OutputSpecs.fs index 88b95a32ed..fec37effca 100644 --- a/tests/Paket.Tests/ProjectFile/OutputSpecs.fs +++ b/tests/Paket.Tests/ProjectFile/OutputSpecs.fs @@ -176,6 +176,15 @@ let ``should maintain order when updating project file items`` () = ] CollectionAssert.AreEqual(expected, actual) +[] +let ``should not fail on Compile Remove entry without Include or Update attribute`` () = + ensureDir () + let projFile = ProjectFile.TryLoad("./ProjectFile/TestData/CompileRemoveOnly.csprojtest").Value + let cache = PackProcessCache.empty + projFile.GetCompileItems false cache + |> Seq.toList + |> shouldEqual [] + [] let ``should remove missing files that exist in the project`` () = ensureDir () diff --git a/tests/Paket.Tests/ProjectFile/TestData/CompileRemoveOnly.csprojtest b/tests/Paket.Tests/ProjectFile/TestData/CompileRemoveOnly.csprojtest new file mode 100644 index 0000000000..56535c851e --- /dev/null +++ b/tests/Paket.Tests/ProjectFile/TestData/CompileRemoveOnly.csprojtest @@ -0,0 +1,11 @@ + + + + TestPaket + netstandard1.4 + + + + + +