Skip to content

Commit

Permalink
fsprojects#913 Implementation for multiple targetFramework
Browse files Browse the repository at this point in the history
  • Loading branch information
bhugot committed Jun 16, 2017
1 parent c24a15c commit edd4c0e
Show file tree
Hide file tree
Showing 6 changed files with 243 additions and 59 deletions.
41 changes: 34 additions & 7 deletions src/Paket.Core/Packaging/NupkgWriter.fs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ open Paket.Xml
open System.Text
open System.Text.RegularExpressions
open System.Xml
open Paket.Requirements

module internal NupkgWriter =

Expand Down Expand Up @@ -83,23 +84,49 @@ module internal NupkgWriter =
frameworkAssembliesList |> List.iter (buildFrameworkReferencesNode >> d.Add)
metadataNode.Add d

let buildDependencyNode (Id, requirement:VersionRequirement) =
let buildDependencyNode (id, requirement:VersionRequirement) =
let dep = XElement(ns + "dependency")
dep.SetAttributeValue(XName.Get "id", Id)
dep.SetAttributeValue(XName.Get "id", id)
let version = requirement.FormatInNuGetSyntax()
if String.IsNullOrEmpty version then
dep.SetAttributeValue(XName.Get "version", "0.0")
else
dep.SetAttributeValue(XName.Get "version", version)
dep

let last3(_,_,c) = c;
let last2(_,b) = b;
let fst3 (a, _, _) = a
let second (a, b, _) = (a,b)

let buildGroupNode (framework:FrameworkIdentifier, add) =
let g = XElement(ns + "group")
g.SetAttributeValue(XName.Get "targetFramework", framework.ToString())
add g
g


let buildDependencyNodes (excludedDependencies, add, dependencyList) =
dependencyList
|> List.filter (fun d -> Set.contains (fst3 d) excludedDependencies |> not)
|> List.map second
|> List.iter (buildDependencyNode >> add)

let buildDependencyNodesByGroup excludedDependencies add dependencyList framework =
let node = buildGroupNode(framework, add)
buildDependencyNodes(excludedDependencies, node.Add, dependencyList)

let buildDependenciesNode excludedDependencies dependencyList =
if List.isEmpty dependencyList then () else
let d = XElement(ns + "dependencies")
dependencyList
|> List.filter (fun d -> Set.contains (fst d) excludedDependencies |> not)
|> List.iter (buildDependencyNode >> d.Add)
metadataNode.Add d

let groups = List.groupBy last3 dependencyList
if groups.Length = 1 && fst groups.Head = None then
buildDependencyNodes(excludedDependencies, d.Add, dependencyList)
else
groups
|> List.iter (fun a -> buildDependencyNodesByGroup excludedDependencies d.Add (last2 a) (fst a).Value)
metadataNode.Add d

let buildReferenceNode (fileName) =
let dep = XElement(ns + "reference")
Expand Down Expand Up @@ -369,7 +396,7 @@ module NuspecExtensions =
references.Groups |> Seq.collect (fun kvp ->
kvp.Value.NugetPackages |> List.choose (fun pkg ->
dependencies.TryGetPackage(kvp.Key,pkg.Name)
|> Option.map (fun verreq -> pkg.Name,verreq.VersionRequirement)))
|> Option.map (fun verreq -> pkg.Name,verreq.VersionRequirement, None)))
|> List.ofSeq
) |> Option.defaultValue []
let projectInfo, optionalInfo = project.GetTemplateMetadata ()
Expand Down
19 changes: 11 additions & 8 deletions src/Paket.Core/Packaging/PackageMetaData.fs
Original file line number Diff line number Diff line change
Expand Up @@ -107,11 +107,13 @@ let (|Valid|Invalid|) md =
Symbols = s }
| _ -> Invalid

let addDependency (templateFile : TemplateFile) (dependency : PackageName * VersionRequirement) =
let firstOf3 (a, _, _) = a

let addDependency (templateFile : TemplateFile) (dependency : PackageName * VersionRequirement * FrameworkIdentifier option) =
match templateFile with
| CompleteTemplate(core, opt) ->
let newDeps =
match opt.Dependencies |> List.tryFind (fun (n,_) -> n = fst dependency) with
match opt.Dependencies |> List.tryFind (fun (n,_,_) -> n = firstOf3 dependency) with
| None -> dependency :: opt.Dependencies
| _ -> opt.Dependencies
{ FileName = templateFile.FileName
Expand Down Expand Up @@ -253,7 +255,7 @@ let findDependencies (dependenciesFile : DependenciesFile) config platform (temp
match core.Version with
| Some v ->
let versionConstraint = if lockDependencies || pinProjectReferences then Specific v else Minimum v
PackageName core.Id, VersionRequirement(versionConstraint, getPreReleaseStatus v)
PackageName core.Id, VersionRequirement(versionConstraint, getPreReleaseStatus v), None
| None -> failwithf "There was no version given for %s." templateFile.FileName
| IncompleteTemplate ->
failwithf "You cannot create a dependency on a template file (%s) with incomplete metadata." templateFile.FileName)
Expand Down Expand Up @@ -357,7 +359,7 @@ let findDependencies (dependenciesFile : DependenciesFile) config platform (temp
| None ->
match version with
| Some v ->
np.Name,VersionRequirement.Parse (v.ToString())
np.Name,VersionRequirement.Parse (v.ToString()) , None
| None ->
if minimumFromLockFile then
let groupName =
Expand All @@ -368,7 +370,7 @@ let findDependencies (dependenciesFile : DependenciesFile) config platform (temp
|> Option.map fst

match groupName with
| None -> np.Name,specificVersionRequirement
| None -> np.Name,specificVersionRequirement, None
| Some groupName ->
let group = lockFile.GetGroup groupName

Expand All @@ -377,9 +379,9 @@ let findDependencies (dependenciesFile : DependenciesFile) config platform (temp
| Some resolvedPackage -> VersionRequirement(GreaterThan resolvedPackage.Version, getPreReleaseStatus resolvedPackage.Version)
| None -> specificVersionRequirement

np.Name,lockedVersion
np.Name,lockedVersion, None
else
np.Name,specificVersionRequirement
np.Name,specificVersionRequirement, None
| Some groupName ->
let dependencyVersionRequirement =
if not lockDependencies then
Expand Down Expand Up @@ -437,7 +439,8 @@ let findDependencies (dependenciesFile : DependenciesFile) config platform (temp
match dependencyVersionRequirement with
| Some installed -> installed
| None -> failwithf "No package with id '%O' installed in group %O." np.Name groupName
np.Name, dep)

np.Name, dep, None)

deps
|> List.fold addDependency withDepsAndIncluded
108 changes: 72 additions & 36 deletions src/Paket.Core/PaketConfigFiles/TemplateFile.fs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ open System.IO
open System.Text.RegularExpressions
open Chessie.ErrorHandling
open Paket.Domain
open Paket.Requirements

module private TemplateParser =
type private ParserState =
Expand Down Expand Up @@ -141,7 +142,7 @@ type OptionalPackagingInfo =
RequireLicenseAcceptance : bool
Tags : string list
DevelopmentDependency : bool
Dependencies : (PackageName * VersionRequirement) list
Dependencies : (PackageName * VersionRequirement * FrameworkIdentifier option) list
ExcludedDependencies : Set<PackageName>
ExcludedGroups : Set<GroupName>
References : string list
Expand Down Expand Up @@ -261,45 +262,80 @@ module internal TemplateFile =
| Some m -> ok m
| None -> failP file "No description line in paket.template file."

let private (|Framework|_|) (line:string) =
match line.Trim() with
| String.RemovePrefix "framework:" _ as trimmed -> Some (FrameworkDetection.Extract(trimmed.Replace("framework: ","")))
| _ -> None

let private (|Empty|_|) (line:string) =
match line.Trim() with
| _ when String.IsNullOrWhiteSpace line -> Some (Empty line)
| String.RemovePrefix "//" _ -> Some (Empty line)
| String.RemovePrefix "#" _ -> Some (Empty line)
| _ -> None
type TargetFrameworkGroup =
{
Framework : FrameworkIdentifier option
Dependencies : (PackageName * VersionRequirement * FrameworkIdentifier option) List}
static member ForNone = { Framework = None ; Dependencies = [] }
static member ForFramework framework = { Framework = framework ; Dependencies = [] }

let private getDependencieByLine (fileName, lockFile:LockFile,currentVersion:SemVerInfo option, specificVersions:Map<string, SemVerInfo>, line:string, framework:FrameworkIdentifier option)=
let reg = Regex(@"(?<id>\S+)(?<version>.*)").Match line
let name = PackageName reg.Groups.["id"].Value
let versionRequirement =
let versionString =
let s = reg.Groups.["version"].Value.Trim()
if s.Contains "CURRENTVERSION" then
match specificVersions.TryFind (string name) with
| Some v -> s.Replace("CURRENTVERSION", string v)
| None ->
match currentVersion with
| Some v -> s.Replace("CURRENTVERSION", string v)
| None -> failwithf "The template file %s contains the placeholder CURRENTVERSION, but no version was given." fileName

elif s.Contains "LOCKEDVERSION" then
match lockFile.Groups.[Constants.MainDependencyGroup].Resolution |> Map.tryFind name with
| Some p -> s.Replace("LOCKEDVERSION", string p.Version)
| None ->
let packages =
lockFile.GetGroupedResolution()
|> Seq.filter (fun kv -> snd kv.Key = name)
|> Seq.toList

match packages with
| [] -> failwithf "The template file %s contains the placeholder LOCKEDVERSION, but no version was given for package %O in paket.lock." fileName name
| [kv] -> s.Replace("LOCKEDVERSION", string kv.Value.Version)
| _ -> failwithf "The template file %s contains the placeholder LOCKEDVERSION, but more than one group contains package %O in paket.lock." fileName name

else s

DependenciesFileParser.parseVersionRequirement versionString
name, versionRequirement, framework

let private getDependenciesByTargetFramework fileName lockFile currentVersion specificVersions (lineNo, state: TargetFrameworkGroup list) line=
match state with
| current::other ->
let lineNo = lineNo + 1
match line with
| Framework framework ->
let group = TargetFrameworkGroup.ForFramework framework::current::other
lineNo, group
| Empty _ -> lineNo, current::other
| _ ->
let dependency = getDependencieByLine(fileName, lockFile, currentVersion, specificVersions, line, current.Framework)
lineNo ,{ current with Dependencies = current.Dependencies @ [dependency] }::other
| [] -> failwithf "Error in paket.dependencies line %d" lineNo

let private getDependencies (fileName, lockFile:LockFile, info : Map<string, string>,currentVersion:SemVerInfo option, specificVersions:Map<string, SemVerInfo>) =
match Map.tryFind "dependencies" info with
| None -> []
| Some d ->
d.Split '\n'
|> Array.map (fun d ->
let reg = Regex(@"(?<id>\S+)(?<version>.*)").Match d
let name = PackageName reg.Groups.["id"].Value
let versionRequirement =
let versionString =
let s = reg.Groups.["version"].Value.Trim()
if s.Contains "CURRENTVERSION" then
match specificVersions.TryFind (string name) with
| Some v -> s.Replace("CURRENTVERSION", string v)
| None ->
match currentVersion with
| Some v -> s.Replace("CURRENTVERSION", string v)
| None -> failwithf "The template file %s contains the placeholder CURRENTVERSION, but no version was given." fileName

elif s.Contains "LOCKEDVERSION" then
match lockFile.Groups.[Constants.MainDependencyGroup].Resolution |> Map.tryFind name with
| Some p -> s.Replace("LOCKEDVERSION", string p.Version)
| None ->
let packages =
lockFile.GetGroupedResolution()
|> Seq.filter (fun kv -> snd kv.Key = name)
|> Seq.toList

match packages with
| [] -> failwithf "The template file %s contains the placeholder LOCKEDVERSION, but no version was given for package %O in paket.lock." fileName name
| [kv] -> s.Replace("LOCKEDVERSION", string kv.Value.Version)
| _ -> failwithf "The template file %s contains the placeholder LOCKEDVERSION, but more than one group contains package %O in paket.lock." fileName name

else s

DependenciesFileParser.parseVersionRequirement versionString

name, versionRequirement)
|> Array.toList
d.Split '\n'
|> Array.fold (getDependenciesByTargetFramework fileName lockFile currentVersion specificVersions) (0, [TargetFrameworkGroup.ForNone])
|> snd
|> List.rev
|> List.collect (fun a -> a.Dependencies)


let private getExcludedDependencies (info : Map<string, string>) =
Expand Down
1 change: 0 additions & 1 deletion src/Paket.Core/Versioning/FrameworkHandling.fs
Original file line number Diff line number Diff line change
Expand Up @@ -439,7 +439,6 @@ module FrameworkDetection =

open Logging
/// parse a string to construct a Netframework, NetCore, NetStandard, or other dotnet identifier
[<Obsolete "Use PlatformMatching.extractPlatforms instead">]
let Extract =
memoize
(fun (path:string) ->
Expand Down
86 changes: 84 additions & 2 deletions tests/Paket.Tests/Packaging/NuspecWriterSpecs.fs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ open FsUnit
open NUnit.Framework
open TestHelpers
open Paket.Domain
open Paket.Requirements

[<Test>]
let ``should serialize core info``() =
Expand Down Expand Up @@ -58,8 +59,89 @@ let ``should serialize dependencies``() =
{ OptionalPackagingInfo.Epmty with
Tags = [ "f#"; "rules" ]
Dependencies =
[ PackageName "Paket.Core", VersionRequirement.Parse "[3.1]"
PackageName "xUnit", VersionRequirement.Parse "2.0" ] }
[ PackageName "Paket.Core", VersionRequirement.Parse "[3.1]", None
PackageName "xUnit", VersionRequirement.Parse "2.0", None ] }

let doc = NupkgWriter.nuspecDoc (core, optional)
doc.ToString()
|> normalizeLineEndings
|> shouldEqual (normalizeLineEndings result)


[<Test>]
let ``#913 should serialize dependencies by group``() =
let result = """<package xmlns="http://schemas.microsoft.com/packaging/2011/10/nuspec.xsd">
<metadata>
<id>Paket.Tests</id>
<version>1.0.0.0</version>
<authors>Two, Authors</authors>
<description>A description</description>
<tags>f# rules</tags>
<dependencies>
<group targetFramework="net35">
<dependency id="Paket.Core" version="[3.1.0]" />
<dependency id="xUnit" version="2.0.0" />
</group>
</dependencies>
</metadata>
</package>"""

let core : CompleteCoreInfo =
{ Id = "Paket.Tests"
Version = SemVer.Parse "1.0.0.0" |> Some
Authors = [ "Two"; "Authors" ]
Description = "A description"
Symbols = false }

let optional =
{ OptionalPackagingInfo.Epmty with
Tags = [ "f#"; "rules" ]
Dependencies =
[ PackageName "Paket.Core", VersionRequirement.Parse "[3.1]", Some(FrameworkIdentifier.DotNetFramework(FrameworkVersion.V3_5))
PackageName "xUnit", VersionRequirement.Parse "2.0", Some(FrameworkIdentifier.DotNetFramework(FrameworkVersion.V3_5)) ] }

let doc = NupkgWriter.nuspecDoc (core, optional)
doc.ToString()
|> normalizeLineEndings
|> shouldEqual (normalizeLineEndings result)

[<Test>]
let ``#913 should serialize dependencies by group with 2 group``() =
let result = """<package xmlns="http://schemas.microsoft.com/packaging/2011/10/nuspec.xsd">
<metadata>
<id>Paket.Tests</id>
<version>1.0.0.0</version>
<authors>Two, Authors</authors>
<description>A description</description>
<tags>f# rules</tags>
<dependencies>
<group targetFramework="net35">
<dependency id="Paket.Core" version="[3.1.0]" />
<dependency id="xUnit" version="2.0.0" />
</group>
<group targetFramework="netstandard13">
<dependency id="Paket.Core" version="[3.1.0]" />
<dependency id="xUnit" version="2.0.0" />
</group>
</dependencies>
</metadata>
</package>"""

let core : CompleteCoreInfo =
{ Id = "Paket.Tests"
Version = SemVer.Parse "1.0.0.0" |> Some
Authors = [ "Two"; "Authors" ]
Description = "A description"
Symbols = false }

let optional =
{ OptionalPackagingInfo.Epmty with
Tags = [ "f#"; "rules" ]
Dependencies =
[ PackageName "Paket.Core", VersionRequirement.Parse "[3.1]", Some(FrameworkIdentifier.DotNetFramework(FrameworkVersion.V3_5))
PackageName "xUnit", VersionRequirement.Parse "2.0", Some(FrameworkIdentifier.DotNetFramework(FrameworkVersion.V3_5))
PackageName "Paket.Core", VersionRequirement.Parse "[3.1]", Some(FrameworkIdentifier.DotNetStandard(DotNetStandardVersion.V1_3))
PackageName "xUnit", VersionRequirement.Parse "2.0", Some(FrameworkIdentifier.DotNetStandard(DotNetStandardVersion.V1_3)) ] }

let doc = NupkgWriter.nuspecDoc (core, optional)
doc.ToString()
Expand Down
Loading

0 comments on commit edd4c0e

Please sign in to comment.