diff --git a/src/Paket.Core/Dependencies/DependenciesFileParser.fs b/src/Paket.Core/Dependencies/DependenciesFileParser.fs index 38be237acb..31e0c53097 100644 --- a/src/Paket.Core/Dependencies/DependenciesFileParser.fs +++ b/src/Paket.Core/Dependencies/DependenciesFileParser.fs @@ -216,6 +216,7 @@ module DependenciesFileParser = | SpecificVersion of bool | CopyContentToOutputDir of CopyToOutputDirectorySettings | GenerateLoadScripts of bool option + | GeneratePathProperty of bool | ReferenceCondition of string | Redirects of BindingRedirectsSettings option | ResolverStrategyForTransitives of ResolverStrategy option @@ -392,6 +393,7 @@ module DependenciesFileParser = | String.EqualsIC "off" | String.EqualsIC "false" -> Some false | _ -> None Some (ParserOptions (ParserOption.GenerateLoadScripts setting)) + | String.RemovePrefix "generate_path_property" trimmed -> (Some (ParserOptions (ParserOption.GeneratePathProperty(trimmed.Replace(":","").Trim() = "true")))) | _ -> None let private (|SourceFile|_|) (line:string) = @@ -486,6 +488,7 @@ module DependenciesFileParser = | OmitContent omit -> { current.Options with Settings = { current.Options.Settings with OmitContent = Some omit } } | ReferenceCondition condition -> { current.Options with Settings = { current.Options.Settings with ReferenceCondition = Some condition } } | GenerateLoadScripts mode -> { current.Options with Settings = { current.Options.Settings with GenerateLoadScripts = mode }} + | GeneratePathProperty mode -> { current.Options with Settings = { current.Options.Settings with GeneratePathProperty = Some mode }} let private parseLine fileName checkDuplicates (lineNo, state: DependenciesGroup list) line = match state with diff --git a/src/Paket.Core/Installation/RestoreProcess.fs b/src/Paket.Core/Installation/RestoreProcess.fs index 03a957a750..aae59e80a8 100644 --- a/src/Paket.Core/Installation/RestoreProcess.fs +++ b/src/Paket.Core/Installation/RestoreProcess.fs @@ -22,6 +22,11 @@ let private combineOmitContent (resolvedSettings: InstallSettings) (packageInsta packageInstallSettings.Settings.OmitContent |> Option.orElse resolvedSettings.OmitContent +/// Combines the generate path property settings from the lock file and a project's references file, so that the project settings take priority +let private combineGeneratePathProperty (resolvedSettings: InstallSettings) (packageInstallSettings: PackageInstallSettings) = + packageInstallSettings.Settings.GeneratePathProperty + |> Option.orElse resolvedSettings.GeneratePathProperty + // "copy_local: true" is being used to set the "PrivateAssets=All" setting for a package. // "copy_local: false" in new SDK format is defined as "ExcludeAssets=runtime". /// Combines the copy_local settings from the lock file and a project's references file @@ -339,8 +344,12 @@ let createPaketPropsFile (lockFile:LockFile) (cliTools:ResolvedPackage seq) (ref if not(allDirectPackages.Contains p.Name) then "Condition=\" '$(ManagePackageVersionsCentrally)' != 'true' \"" else "" + let generatePathPropertyAttribute = + match combineGeneratePathProperty p.Settings packageSettings with + | Some true -> "GeneratePathProperty=\"true\"" + | _ -> "" - [yield sprintf """ """ directReferenceCondition p.Name + [yield sprintf """ """ directReferenceCondition generatePathPropertyAttribute p.Name yield sprintf """ %O""" p.Version let excludeAssets = [ if combineCopyLocal p.Settings packageSettings = Some false then yield "runtime" @@ -471,6 +480,7 @@ let createProjectReferencesFiles (lockFile:LockFile) (projectFile:ProjectFile) ( let combinedCopyLocal = combineCopyLocal resolvedPackage.Settings packageSettings let combinedOmitContent = combineOmitContent resolvedPackage.Settings packageSettings let combinedImportTargets = combineImportTargets resolvedPackage.Settings packageSettings + let combinedGeneratePathProperty = combineGeneratePathProperty resolvedPackage.Settings packageSettings let aliases = if direct then packageSettings.Settings.Aliases |> Seq.tryHead else None let privateAssetsAll = @@ -492,11 +502,15 @@ let createProjectReferencesFiles (lockFile:LockFile) (projectFile:ProjectFile) ( match combinedImportTargets with | Some false -> "false" | _ -> "true" - let alias = match aliases with | Some(x) -> x.Value | _ -> "" + let generatePathProperty = + match combinedGeneratePathProperty with + | Some true -> "true" + | _ -> "false" + let line = [ packageName.ToString() package.Version.ToString() @@ -506,7 +520,8 @@ let createProjectReferencesFiles (lockFile:LockFile) (projectFile:ProjectFile) ( copyLocal omitContent importTargets - alias] + alias + generatePathProperty] |> String.concat "," list.Add line diff --git a/src/Paket.Core/PaketConfigFiles/LockFile.fs b/src/Paket.Core/PaketConfigFiles/LockFile.fs index 91006224e4..a060091c78 100644 --- a/src/Paket.Core/PaketConfigFiles/LockFile.fs +++ b/src/Paket.Core/PaketConfigFiles/LockFile.fs @@ -91,7 +91,11 @@ module LockFileSerializer = match options.Settings.FrameworkRestrictions |> getExplicitRestriction with | FrameworkRestriction.HasNoRestriction -> () - | list -> yield "RESTRICTION: " + list.ToString() ] + | list -> yield "RESTRICTION: " + list.ToString() + + match options.Settings.GeneratePathProperty with + | Some x -> yield "GENERATE-PATH-PROPERTY: " + x.ToString().ToUpper() + | None -> () ] /// [omit] let serializePackages options (resolved : PackageResolution) = @@ -268,6 +272,7 @@ module LockFileParser = | ImportTargets of bool | LicenseDownload of bool | GenerateLoadScripts of bool option + | GeneratePathProperty of bool | FrameworkRestrictions of FrameworkRestrictions | CopyLocal of bool | SpecificVersion of bool @@ -322,6 +327,7 @@ module LockFileParser = | _ -> None InstallOption (GenerateLoadScripts setting) + | _, String.RemovePrefix "GENERATE-PATH-PROPERTY:" trimmed -> InstallOption(GeneratePathProperty(trimmed.Trim() = "TRUE")) | _, String.RemovePrefix "COPY-CONTENT-TO-OUTPUT-DIR:" trimmed -> let setting = match trimmed.Replace(":","").Trim().ToLowerInvariant() with @@ -411,6 +417,7 @@ module LockFileParser = | CopyContentToOutputDir mode -> { currentGroup.Options with Settings = { currentGroup.Options.Settings with CopyContentToOutputDirectory = Some mode }} | FrameworkRestrictions r -> { currentGroup.Options with Settings = { currentGroup.Options.Settings with FrameworkRestrictions = r }} | OmitContent omit -> { currentGroup.Options with Settings = { currentGroup.Options.Settings with OmitContent = Some omit }} + | GeneratePathProperty mode -> { currentGroup.Options with Settings = { currentGroup.Options.Settings with GeneratePathProperty = Some mode }} | GenerateLoadScripts mode -> { currentGroup.Options with Settings = { currentGroup.Options.Settings with GenerateLoadScripts = mode }} | ReferenceCondition condition -> { currentGroup.Options with Settings = { currentGroup.Options.Settings with ReferenceCondition = Some condition }} | DirectDependenciesResolverStrategy strategy -> { currentGroup.Options with ResolverStrategyForDirectDependencies = strategy } diff --git a/src/Paket.Core/PaketConfigFiles/ReferencesFile.fs b/src/Paket.Core/PaketConfigFiles/ReferencesFile.fs index 46bd58a221..6bd970f95a 100644 --- a/src/Paket.Core/PaketConfigFiles/ReferencesFile.fs +++ b/src/Paket.Core/PaketConfigFiles/ReferencesFile.fs @@ -130,7 +130,7 @@ type ReferencesFile = { ReferencesFile.FromLines lines with FileName = fileName } with e -> raise (new Exception(sprintf "Could not parse reference file '%s': %s" fileName e.Message, e)) - member this.AddNuGetReference(groupName, packageName : PackageName, embedInteropTypes: bool, copyLocal: bool, specificVersion: bool, importTargets: bool, frameworkRestrictions, includeVersionInPath, downloadLicense, omitContent : bool, createBindingRedirects, referenceCondition) = + member this.AddNuGetReference(groupName, packageName : PackageName, embedInteropTypes: bool, copyLocal: bool, specificVersion: bool, importTargets: bool, frameworkRestrictions, includeVersionInPath, downloadLicense, omitContent : bool, createBindingRedirects, referenceCondition, generatePathProperty) = let package: PackageInstallSettings = { Name = packageName Settings = @@ -149,6 +149,7 @@ type ReferencesFile = Aliases = Map.empty OmitContent = if omitContent then Some ContentCopySettings.Omit else None GenerateLoadScripts = None + GeneratePathProperty = if generatePathProperty then Some generatePathProperty else None Simplify = None } } match this.Groups |> Map.tryFind groupName with @@ -175,7 +176,7 @@ type ReferencesFile = { this with Groups = newGroups } member this.AddNuGetReference(groupName, packageName : PackageName) = - this.AddNuGetReference(groupName, packageName, false, true, true, true, ExplicitRestriction FrameworkRestriction.NoRestriction, false, false, false, None, null) + this.AddNuGetReference(groupName, packageName, false, true, true, true, ExplicitRestriction FrameworkRestriction.NoRestriction, false, false, false, None, null, false) member this.RemoveNuGetReference(groupName, packageName : PackageName) = let group = this.Groups.[groupName] @@ -215,4 +216,4 @@ type ReferencesFile = if g.Key <> Constants.MainDependencyGroup then if g.Value.NugetPackages <> [] || g.Value.RemoteFiles <> [] then yield "group " + g.Key.ToString() - yield! printGroup g.Value|]) \ No newline at end of file + yield! printGroup g.Value|]) diff --git a/src/Paket.Core/Versioning/Requirements.fs b/src/Paket.Core/Versioning/Requirements.fs index 6119ab8343..07d2e165c0 100644 --- a/src/Paket.Core/Versioning/Requirements.fs +++ b/src/Paket.Core/Versioning/Requirements.fs @@ -922,6 +922,7 @@ type InstallSettings = Aliases : Map CopyContentToOutputDirectory : CopyToOutputDirectorySettings option GenerateLoadScripts : bool option + GeneratePathProperty : bool option Simplify : bool option } static member Default = @@ -940,6 +941,7 @@ type InstallSettings = CopyContentToOutputDirectory = None OmitContent = None GenerateLoadScripts = None + GeneratePathProperty = None Simplify = None } member this.ToString(groupSettings:InstallSettings,asLines) = @@ -992,6 +994,10 @@ type InstallSettings = | Some true when groupSettings.GenerateLoadScripts <> this.GenerateLoadScripts -> yield "generate_load_scripts: true" | Some false when groupSettings.GenerateLoadScripts <> this.GenerateLoadScripts -> yield "generate_load_scripts: false" | _ -> () + match this.GeneratePathProperty with + | Some true when groupSettings.GeneratePathProperty <> this.GeneratePathProperty -> yield "generate_path_property: true" + | Some false when groupSettings.GeneratePathProperty <> this.GeneratePathProperty -> yield "generate_path_property: false" + | _ -> () match this.Simplify with | Some false -> yield "simplify: false" | _ -> () ] @@ -1103,6 +1109,11 @@ type InstallSettings = | Some "on" | Some "true" -> Some true | Some "off" | Some "false" -> Some true | _ -> None + GeneratePathProperty = + match getPair "generate_path_property" with + | Some "on" | Some "true" -> Some true + | Some "off" | Some "false" -> Some true + | _ -> None Simplify = match getPair "simplify" with | Some "never" | Some "false" -> Some false @@ -1333,4 +1344,4 @@ let addFrameworkRestrictionsToDependencies rawDependencies (frameworkGroups:Pars referenced |> List.map (fun (a,b,c) -> a,b, ExplicitRestriction c), - problems.ToArray() \ No newline at end of file + problems.ToArray() diff --git a/tests/Paket.Tests/DependenciesFile/ParserSpecs.fs b/tests/Paket.Tests/DependenciesFile/ParserSpecs.fs index 1a5914bc7b..bff5c9fa11 100644 --- a/tests/Paket.Tests/DependenciesFile/ParserSpecs.fs +++ b/tests/Paket.Tests/DependenciesFile/ParserSpecs.fs @@ -1682,6 +1682,17 @@ let ``parsing generate load scripts`` () = printfn "case %A expected %A got %A" case expectation result failwith "failed" +let generatePathPropertyConfig = """ +generate_path_property: true +source https://www.nuget.org/api/v2 + +nuget FAKE +""" + +[] +let ``should read generate_path_property config``() = + let cfg = DependenciesFile.FromSource(generatePathPropertyConfig) + cfg.Groups.[Constants.MainDependencyGroup].Options.Settings.GeneratePathProperty |> shouldEqual (Some true) let configWithCLitTool = """ source https://www.nuget.org/api/v2 diff --git a/tests/Paket.Tests/InstallModel/PaketPropsTests.fs b/tests/Paket.Tests/InstallModel/PaketPropsTests.fs index f65e7042e1..830b2cf0a8 100644 --- a/tests/Paket.Tests/InstallModel/PaketPropsTests.fs +++ b/tests/Paket.Tests/InstallModel/PaketPropsTests.fs @@ -116,6 +116,43 @@ let checkContainsPackageRefsCondition pkgRefs (group: XElement) = | None -> Assert.Fail(sprintf "expected package '%s' with condition '%O' not found in '%A' group" pkgName pkgCondition group) +let checkHasGeneratePathPropertyAttribute pkgRefs (group: XElement) = + let isPackageReference name (x: XElement) = + if x.Name = (xname "PackageReference") then + match x.Attribute(XName.Get "Include") with + | null -> false + | v -> v.Value = name + else + false + + let hasVersion version (x: XElement) = + x.Elements(xname "Version") + |> Seq.tryHead + |> Option.map (fun x -> x.Value = version) + |> Option.exists id + + let hasGeneratePathProperty (x: XElement) = + if x.Name = (xname "PackageReference") then + match x.Attribute(XName.Get "GeneratePathProperty") with + | null -> false + | v -> v.Value = "true" + else + false + + let packageRefs = group.Elements(xname "PackageReference") |> Seq.toList + Assert.AreEqual(pkgRefs |> List.length, packageRefs |> Seq.length, (sprintf "%A" group)) + for pkgName, pkgVersion in pkgRefs do + let pkg = + packageRefs + |> List.filter (isPackageReference pkgName) + |> List.filter (hasVersion pkgVersion) + |> List.filter (hasGeneratePathProperty) + |> List.tryHead + match pkg with + | Some p -> () + | None -> + Assert.Fail(sprintf "expected package '%s' with GeneratePathProperty=true not found in '%A' group" pkgName group) + [] let ``should create props file for design mode``() = @@ -462,3 +499,39 @@ Newtonsoft.Json |> checkContainsPackageRefs [ "Newtonsoft.Json","11.0.2" ] | l -> Assert.Fail(sprintf "expected one ItemGroup but was '%A'" l) + + +[] +let ``should create props file with GeneratePathProperty``() = + + let lockFile = """NUGET + remote: https://api.nuget.org/v3/index.json + Newtonsoft.Json (11.0.2) - generate_path_property: true +""" + + let refFileContent = """ +Newtonsoft.Json +""" + + let lockFile = LockFile.Parse("", toLines lockFile) + + let refFile = ReferencesFile.FromLines(toLines refFileContent) + + let packages = + [ for kv in refFile.Groups do + let packagesInGroup,_ = lockFile.GetOrderedPackageHull(kv.Key, refFile) + yield! packagesInGroup ] + + let outPath = System.IO.Path.GetTempFileName() + Paket.RestoreProcess.createPaketPropsFile lockFile Seq.empty refFile packages (FileInfo outPath) + + let doc = XDocument.Load(outPath, LoadOptions.PreserveWhitespace) + + let itemGroups = doc.Root.Elements (xname "ItemGroup") |> Seq.toList + + match itemGroups with + | [groupMain] -> + groupMain + |> checkHasGeneratePathPropertyAttribute [ "Newtonsoft.Json","11.0.2" ] + | l -> + Assert.Fail(sprintf "expected one ItemGroup but was '%A'" l) diff --git a/tests/Paket.Tests/Lockfile/ParserSpecs.fs b/tests/Paket.Tests/Lockfile/ParserSpecs.fs index fadee33ec9..b3d0249c0d 100644 --- a/tests/Paket.Tests/Lockfile/ParserSpecs.fs +++ b/tests/Paket.Tests/Lockfile/ParserSpecs.fs @@ -347,7 +347,7 @@ let frameworkRestricted' = """NUGET ReadOnlyCollectionInterfaces (1.0.0) - framework: >= net40 System.Json (>= 4.0.20126.16343) FsControl (1.0.9) - FSharpPlus (0.0.4) + FSharpPlus (0.0.4) - generate_path_property: true FsControl (>= 1.0.9) LinqBridge (1.3.0) - import_targets: false, content: none, version_in_path: true, framework: >= net20 < net35, copy_content_to_output_dir: never ReadOnlyCollectionExtensions (1.2.0) @@ -370,6 +370,8 @@ let ``should parse framework restricted lock file in new syntax``() = packages.[0].Settings.LicenseDownload |> shouldEqual (Some true) + packages.[2].Settings.GeneratePathProperty |> shouldEqual (Some true) + packages.[3].Source |> shouldEqual PackageSources.DefaultNuGetSource packages.[3].Name |> shouldEqual (PackageName "LinqBridge") packages.[3].Version |> shouldEqual (SemVer.Parse "1.3.0") @@ -383,6 +385,7 @@ let ``should parse framework restricted lock file in new syntax``() = packages.[3].Settings.IncludeVersionInPath |> shouldEqual (Some true) packages.[3].Settings.LicenseDownload |> shouldEqual None packages.[3].Settings.OmitContent |> shouldEqual (Some ContentCopySettings.Omit) + packages.[3].Settings.GeneratePathProperty |> shouldEqual None let dependencies4 = packages.[4].Dependencies |> Set.toList |> List.map (fun (_, _, r) -> r) @@ -588,6 +591,7 @@ let groupsLockFile = """REDIRECTS: ON COPY-LOCAL: TRUE IMPORT-TARGETS: TRUE LICENSE-DOWNLOAD: TRUE +GENERATE-PATH-PROPERTY: TRUE NUGET remote: "D:\code\temp with space" Castle.Windsor (2.1) @@ -614,6 +618,7 @@ let ``should parse lock file with groups``() = lockFile1.Options.Settings.LicenseDownload |> shouldEqual (Some true) lockFile1.Options.Settings.CopyLocal |> shouldEqual (Some true) lockFile1.Options.Settings.ReferenceCondition |> shouldEqual None + lockFile1.Options.Settings.GeneratePathProperty |> shouldEqual (Some true) packages1.Head.Source.Url |> shouldEqual "D:\code\\temp with space" packages1.[0].Name |> shouldEqual (PackageName "Castle.Windsor")