diff --git a/src/ce_dubproject.pas b/src/ce_dubproject.pas index 14bdb690..d38198e8 100644 --- a/src/ce_dubproject.pas +++ b/src/ce_dubproject.pas @@ -6,7 +6,7 @@ interface uses Classes, SysUtils, fpjson, jsonparser, jsonscanner, process, strutils, - LazFileUtils, RegExpr, + LazFileUtils, RegExpr, fgl, ce_common, ce_interfaces, ce_observer, ce_dialogs, ce_processes, ce_writableComponent, ce_compilers, ce_semver, ce_stringrange; @@ -22,11 +22,20 @@ type PDubLocalPackage = ^TDubLocalPackage; - TDubLocalPackage = record - name : string; - versions: array of TSemVer; + TSemVerList = specialize TFPGList; + + TDubLocalPackage = class + strict private + fName : string; + fVersions: TSemVerList; + public + constructor create; + destructor destroy; override; procedure addVersion(const value: string); - function findVersion(constref value: TSemVer): boolean; + function findVersion(constref value: TSemVer): PSemVer; + function highestInInterval(constref lo, hi: TSemVer): PSemVer; + function highest: PSemVer; + property name: string read fName write fName; end; TDubLocalPackages = class @@ -34,8 +43,11 @@ type fRoot: string; fLocalPackages: array of TDubLocalPackage; public + destructor destroy; override; procedure update; - function find(const name: string; out package: PDubLocalPackage): boolean; + function find(const name: string; out package: PDubLocalPackage): boolean; overload; + function find(const name, op: string; constref opVer: TSemVer; + out package: PDubLocalPackage): PSemver; overload; function fetch(constref version: TSemVer): PDubLocalPackage; function getPackageePath(package: PDubLocalPackage): string; end; @@ -212,35 +224,86 @@ const DubDefaultConfigName = '(default config)'; {$REGION TDubLocalPackages -----------------------------------------------------} -procedure TDubLocalPackage.addVersion(const value: string); -var - v: TSemVer; - i: integer; +constructor TDubLocalPackage.create; begin - if value = 'vmaster' then - v.init('v0.0.0-master') - else try - v.init(value); - except - exit; - end; - for i:= 0 to high(versions) do - begin - if versions[i] = v then - exit; - end; - setLength(versions, length(versions) + 1); - versions[high(versions)] := v; + fVersions := TSemVerList.create; end; -function TDubLocalPackage.findVersion(constref value: TSemVer): boolean; +destructor TDubLocalPackage.destroy; var i: integer; begin - result := false; - for i:= 0 to high(versions) do - if versions[i] = value then - exit(true); + for i := 0 to fVersions.Count-1 do + dispose(fVersions.Items[i]); + fVersions.Free; + inherited; +end; + +procedure TDubLocalPackage.addVersion(const value: string); +var + v: PSemVer; + i: integer; +begin + v := new(PSemVer); + if value = 'vmaster' then + v^.init('v0.0.0-master') + else try + v^.init(value); + except + dispose(v); + exit; + end; + for i := 0 to fVersions.Count-1 do + begin + if fVersions[i]^ = v^ then + exit; + if (i < fVersions.Count-1) and (fVersions[i+1]^ > v^) and (fVersions[i]^ < v^ ) then + begin + fVersions.Insert(i, v); + exit; + end; + end; + fVersions.Add(v); +end; + +function TDubLocalPackage.highest: PSemVer; +begin + result := fVersions.Last; +end; + +function TDubLocalPackage.highestInInterval(constref lo, hi: TSemVer): PSemVer; +var + i: integer; +begin + result := nil; + for i := 0 to fVersions.Count-1 do + begin + if fVersions[i]^ < lo then + continue; + if fVersions[i]^ < hi then + result := fVersions[i]; + if (fVersions[i]^ > hi) then + break; + end; +end; + +function TDubLocalPackage.findVersion(constref value: TSemVer): PSemVer; +var + i: integer; +begin + result := nil; + for i:= 0 to fVersions.Count-1 do + if fVersions.Items[i]^ = value then + exit(fVersions.Items[i]); +end; + +destructor TDubLocalPackages.destroy; +var + i: integer; +begin + for i:= 0 to high(fLocalPackages) do + fLocalPackages[i].Free; + inherited; end; procedure TDubLocalPackages.update; @@ -291,6 +354,7 @@ begin if not find(n, d) then begin setLength(fLocalPackages, length(fLocalPackages) + 1); + fLocalPackages[high(fLocalPackages)] := TDubLocalPackage.create; d := @fLocalPackages[high(fLocalPackages)]; d^.name := n; end; @@ -318,6 +382,54 @@ begin end; end; +function TDubLocalPackages.find(const name, op: string; constref opVer: TSemVer; + out package: PDubLocalPackage): PSemVer; +var + hi: TSemVer; +begin + result := nil; + if op = '=' then + begin + if find(name, package) then + result := package^.findVersion(opVer); + end + else if op = '>=' then + begin + if find(name, package) then + begin + result := package^.highest; + if result^ < opVer then + result := nil; + end; + end + else if op = '>' then + begin + if find(name, package) then + begin + result := package^.highest; + if (result^ < opVer) or (result^ = opVer) then + result := nil; + end; + end + else if op = '~>' then + begin + if find(name, package) then + begin + hi := opVer; + hi.minor := hi.minor + 1; + hi.patch := 0; + hi.additional :=''; + result := package^.highestInInterval(opVer, hi); + result := result; + end; + end + else + begin + if find(name, package) then + result := package^.highest; + end; +end; + function TDubLocalPackages.fetch(constref version: TSemVer): PDubLocalPackage; begin result := nil; @@ -325,9 +437,8 @@ end; function TDubLocalPackages.getPackageePath(package: PDubLocalPackage): string; begin - result := fRoot + package^.name; + result := fRoot + package^.Name; end; - {$ENDREGION} {$REGION Options ---------------------------------------------------------------} @@ -1173,6 +1284,7 @@ procedure TCEDubProject.updateImportPathsFromJson; z: string; r: TStringRange = (ptr: nil; pos: 0; len: 0); q: TSemVer; + u: PSemVer; i: integer; begin if obj.findObject('dependencies', deps) then @@ -1201,10 +1313,10 @@ procedure TCEDubProject.updateImportPathsFromJson; else q.init('v' + p); - if fLocalPackages.find(n, pck) and - pck^.findVersion(q) then + u := fLocalPackages.find(n, o, q, pck); + if assigned(u) then begin - p := s + '-' + p + DirectorySeparator + n + DirectorySeparator; + p := s + '-' + u^.asString + DirectorySeparator + n + DirectorySeparator; if (p + 'source').dirExists then fImportPaths.Add(p + 'source') else if (p + 'src').dirExists then diff --git a/src/ce_semver.pas b/src/ce_semver.pas index f724da33..160e2b88 100644 --- a/src/ce_semver.pas +++ b/src/ce_semver.pas @@ -31,14 +31,17 @@ type // Indicates wether the version is a release candidate ("v1.0.0-rc", "v1.0.0-rc.1"). function isRc: boolean; + // Rebuild and Returns the SemVer string. + function asString: string; + // The major version. - property major: word read fMajor; + property major: word read fMajor write fMajor; // The minor version. - property minor: word read fMinor; + property minor: word read fMinor write fMinor; // The patch. - property patch: word read fPatch; + property patch: word read fPatch write fPatch; // The additional labels. - property additional: string read fAdditional; + property additional: string read fAdditional write fAdditional; // Indicates if the init has succeeded. property valid: boolean read fValid; end; @@ -46,6 +49,7 @@ type PSemVer = ^TSemVer; operator > (constref lhs, rhs: TSemVer): boolean; + operator < (constref lhs, rhs: TSemVer): boolean; operator = (constref lhs, rhs: TSemVer): boolean; implementation @@ -56,12 +60,18 @@ var v1, v2: TSemVer; procedure TSemVer.init(const text: string; throw: boolean = true); - procedure error(const message: string); + procedure resetFields(); begin fMajor := 0; fMinor := 0; fPatch := 0; fAdditional := ''; + end; + + procedure error(const message: string); + begin + resetFields(); + fAdditional := ''; fValid := false; raise Exception.Create(message); end; @@ -69,6 +79,7 @@ procedure TSemVer.init(const text: string; throw: boolean = true); var r: TStringRange = (ptr:nil; pos:0; len: 0); begin + resetFields(); if throw and (text.length < 6) then error('Invalid semVer format, at least 6 characters expected'); r.init(text); @@ -107,6 +118,13 @@ begin result := (additional = 'rc') or (additional.StartsWith('rc.')); end; +function TSemVer.asString: string; +begin + result := format('%d.%d.%d', [fMajor, fMinor, fPatch]); + if fAdditional <> '' then + result += '-' + fAdditional; +end; + operator > (constref lhs, rhs: TSemVer): boolean; begin result := (lhs.major > rhs.major) or @@ -117,6 +135,11 @@ begin and (lhs.patch = rhs.patch) and (lhs.additional > rhs.additional)); end; +operator < (constref lhs, rhs: TSemVer): boolean; +begin + result := rhs > lhs; +end; + operator = (constref lhs, rhs: TSemVer): boolean; begin result := (lhs.major = rhs.major) and (lhs.minor = rhs.minor) @@ -133,24 +156,29 @@ begin v1.init('v2.0.0'); v2.init('v1.0.0'); assert(v1 > v2); + assert(v2 < v1); v1.init('v1.1.0'); v2.init('v1.0.0'); assert(v1 > v2); + assert(v2 < v1); v1.init('v1.1.1'); v2.init('v1.1.0'); assert(v1 > v2); + assert(v2 < v1); v1.init('v1.1.1'); v2.init('v1.0.1'); assert(v1 > v2); + assert(v2 < v1); v1.init('v1.1.1-alpha.2'); v2.init('v1.1.1-alpha.1'); assert(v1 > v2); assert(v1.isAlpha); assert(v2.isAlpha); + assert(v2.asString = '1.1.1-alpha.1'); v1.init('v1.1.1-beta.1'); v2.init('v1.1.1-alpha.8'); @@ -167,6 +195,7 @@ begin assert(v2.major = 1); assert(v2.minor = 22); assert(v2.patch = 33); + assert(v2.asString = '1.22.33'); v1.init('v0.0.2060'); assert(v1.major = 0);