handle dependency operators, close #231

This commit is contained in:
Basile Burg 2018-02-07 09:08:27 +01:00
parent 7ff81260ea
commit d3c495d605
2 changed files with 180 additions and 39 deletions

View File

@ -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<PSemVer>;
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

View File

@ -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);