mirror of
https://github.com/boostorg/build.git
synced 2026-07-21 13:13:39 +00:00
Experimental "version" feature type.
This adds a "version" type attribute to features that follow semver semantics. The implementation follows the semver rules for matching property sets and requirements, and for matching target alternatives. The implementation relies on the bfgroup/versioned library for the semver parsing and checking.
This commit is contained in:
@@ -14,6 +14,9 @@
|
|||||||
* *New*: Add reading/parsing of property database from JSON data to base CPS
|
* *New*: Add reading/parsing of property database from JSON data to base CPS
|
||||||
support from.
|
support from.
|
||||||
-- _René Ferdinand Rivera Morell_
|
-- _René Ferdinand Rivera Morell_
|
||||||
|
* *New*: Add `version` feature attribute to tag and manage semantic versioning
|
||||||
|
of targets.
|
||||||
|
-- _René Ferdinand Rivera Morell_
|
||||||
* Fix Jam/{CPP} bind definitions of 4 or more values in a single
|
* Fix Jam/{CPP} bind definitions of 4 or more values in a single
|
||||||
declared argument not actually adding all the definitions.
|
declared argument not actually adding all the definitions.
|
||||||
-- _Paolo Pastori_
|
-- _Paolo Pastori_
|
||||||
|
|||||||
@@ -1091,6 +1091,16 @@ on library B. As the result, whenever an application will link to A, it
|
|||||||
will also link to B. Specifying B as dependency of A is different from
|
will also link to B. Specifying B as dependency of A is different from
|
||||||
adding B to the sources of A.
|
adding B to the sources of A.
|
||||||
|
|
||||||
|
* [[b2.reference.features.attributes.version]] _version_
|
||||||
|
+
|
||||||
|
The value of a version feature is a valid Semantic Version.
|
||||||
|
footnote:[Semantic Versioning (https://semver.org/)] Like free features, the
|
||||||
|
set of values are unbounded. When a version feature is specified it will use
|
||||||
|
the semnantic versioning rules to match them against each other.
|
||||||
|
+
|
||||||
|
_Note, this is an experimental feature and how it works is subject to future
|
||||||
|
changes as use cases get discovered._
|
||||||
|
|
||||||
[[b2.reference.features.attributes.base]]
|
[[b2.reference.features.attributes.base]]
|
||||||
Features that are neither free nor incidental are called _base_
|
Features that are neither free nor incidental are called _base_
|
||||||
features.
|
features.
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
// Copyright (c) 2003 Vladimir Prus
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See
|
||||||
|
// accompanying file LICENSE.txt or copy at
|
||||||
|
// https://www.bfgroup.xyz/b2/LICENSE.txt)
|
||||||
|
|
||||||
|
// tag::source[]
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
std::cout << "Hello!\n";
|
||||||
|
}
|
||||||
|
// end::source[]
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
lib ext : : <name>ext10 <version>1.0 ;
|
||||||
|
lib ext : : <name>ext12 <version>1.2 ;
|
||||||
|
lib ext : : <name>ext20 <version>2.0 ;
|
||||||
|
explicit ext ;
|
||||||
|
|
||||||
|
exe hello : hello.cpp ext/<version>2 : <version>2.0 ;
|
||||||
|
exe hello : hello.cpp ext/<version>1 : <version>1.1 ;
|
||||||
|
explicit hello ;
|
||||||
|
|
||||||
|
explicit
|
||||||
|
[ exe hello20 : hello.cpp ext/<version>2 : <version>2.0 ]
|
||||||
|
[ exe hello11 : hello.cpp ext/<version>1.1 : <version>1.1 ]
|
||||||
|
[ exe hello10 : hello.cpp ext/<version>1.0 : <version>1.0 ] ;
|
||||||
|
|
||||||
|
alias all : hello/<version>2 hello/<version>1 hello20 hello11 hello10 ;
|
||||||
+57
-10
@@ -1,5 +1,5 @@
|
|||||||
# Copyright 2001, 2002, 2003 Dave Abrahams
|
# Copyright 2001, 2002, 2003 Dave Abrahams
|
||||||
# Copyright 2002, 2006 Rene Rivera
|
# Copyright 2002, 2006, 2026 Rene Rivera
|
||||||
# Copyright 2002, 2003, 2004, 2005, 2006 Vladimir Prus
|
# Copyright 2002, 2003, 2004, 2005, 2006 Vladimir Prus
|
||||||
# Distributed under the Boost Software License, Version 1.0.
|
# Distributed under the Boost Software License, Version 1.0.
|
||||||
# (See accompanying file LICENSE.txt or copy at
|
# (See accompanying file LICENSE.txt or copy at
|
||||||
@@ -29,6 +29,7 @@ local rule setup ( )
|
|||||||
subfeature
|
subfeature
|
||||||
order-sensitive
|
order-sensitive
|
||||||
hidden
|
hidden
|
||||||
|
version
|
||||||
;
|
;
|
||||||
|
|
||||||
.all-features = ;
|
.all-features = ;
|
||||||
@@ -113,14 +114,22 @@ rule feature (
|
|||||||
{
|
{
|
||||||
error = feature already "defined:" ;
|
error = feature already "defined:" ;
|
||||||
}
|
}
|
||||||
else if implicit in $(attributes) && free in $(attributes)
|
else if free in $(attributes) && implicit in $(attributes)
|
||||||
{
|
{
|
||||||
error = free features cannot also be implicit ;
|
error = free features cannot be implicit ;
|
||||||
}
|
}
|
||||||
else if free in $(attributes) && propagated in $(attributes)
|
else if free in $(attributes) && propagated in $(attributes)
|
||||||
{
|
{
|
||||||
error = free features cannot be propagated ;
|
error = free features cannot be propagated ;
|
||||||
}
|
}
|
||||||
|
else if version in $(attributes) && implicit in $(attributes)
|
||||||
|
{
|
||||||
|
error = version features cannot be implicit ;
|
||||||
|
}
|
||||||
|
else if version in $(attributes) && free in $(attributes)
|
||||||
|
{
|
||||||
|
error = version features cannot be free ;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if [ MATCH (=) : $(values) ]
|
if [ MATCH (=) : $(values) ]
|
||||||
@@ -170,6 +179,10 @@ rule set-default ( feature : value )
|
|||||||
{
|
{
|
||||||
bad-attribute = optional ;
|
bad-attribute = optional ;
|
||||||
}
|
}
|
||||||
|
else if version in $(a)
|
||||||
|
{
|
||||||
|
bad-attribute = version ;
|
||||||
|
}
|
||||||
if $(bad-attribute)
|
if $(bad-attribute)
|
||||||
{
|
{
|
||||||
import errors ;
|
import errors ;
|
||||||
@@ -194,7 +207,7 @@ rule defaults ( features * )
|
|||||||
{
|
{
|
||||||
local gf = $(:E=:G=$(f)) ;
|
local gf = $(:E=:G=$(f)) ;
|
||||||
local a = $($(gf).attributes) ;
|
local a = $($(gf).attributes) ;
|
||||||
if ( free in $(a) ) || ( optional in $(a) )
|
if ( free in $(a) ) || ( optional in $(a) ) || ( version in $(a) )
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -462,7 +475,18 @@ local rule extend-feature ( feature : values * )
|
|||||||
#
|
#
|
||||||
rule validate-value-string ( feature value-string )
|
rule validate-value-string ( feature value-string )
|
||||||
{
|
{
|
||||||
if ! (
|
if ( version in $($(feature).attributes) )
|
||||||
|
{
|
||||||
|
# Check that the value is a valid semver.
|
||||||
|
import version ;
|
||||||
|
if ! [ version.semver-verify $(value-string) ]
|
||||||
|
{
|
||||||
|
import errors ;
|
||||||
|
errors.error \"$(value-string)\"
|
||||||
|
"is not a valid semantic version for feature" \"$(feature)\" ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if ! (
|
||||||
free in $($(feature).attributes)
|
free in $($(feature).attributes)
|
||||||
|| ( $(value-string) in $($(feature).values) )
|
|| ( $(value-string) in $($(feature).values) )
|
||||||
)
|
)
|
||||||
@@ -738,6 +762,20 @@ rule free-features ( )
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#|
|
||||||
|
Is a feature with a unbounded/variable set of values, like free and version.
|
||||||
|
|#
|
||||||
|
local rule is-variable-feature ( f )
|
||||||
|
{
|
||||||
|
local attr = [ attributes $(f) ] ;
|
||||||
|
if free in $(attr) || feature in $(attr)
|
||||||
|
{
|
||||||
|
return true ;
|
||||||
|
}
|
||||||
|
return ;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
# Expand all composite properties in the set so that all components are
|
# Expand all composite properties in the set so that all components are
|
||||||
# explicitly expressed.
|
# explicitly expressed.
|
||||||
#
|
#
|
||||||
@@ -757,7 +795,7 @@ rule expand-composites ( properties * )
|
|||||||
{
|
{
|
||||||
local f = $(x:G) ;
|
local f = $(x:G) ;
|
||||||
|
|
||||||
if $(f) in $(free.features)
|
if $(f) in $(free.features) || $(f) in $(version.features)
|
||||||
{
|
{
|
||||||
result += $(x) ;
|
result += $(x) ;
|
||||||
}
|
}
|
||||||
@@ -1085,7 +1123,7 @@ rule add-defaults ( properties * )
|
|||||||
local to-remove ;
|
local to-remove ;
|
||||||
for local f in $(properties:G)
|
for local f in $(properties:G)
|
||||||
{
|
{
|
||||||
if ! free in [ attributes $(f) ]
|
if ! [ is-variable-feature $(f) ] && ! feature in [ attributes $(f) ]
|
||||||
{
|
{
|
||||||
to-remove += $(f) ;
|
to-remove += $(f) ;
|
||||||
}
|
}
|
||||||
@@ -1112,9 +1150,10 @@ rule add-defaults ( properties * )
|
|||||||
{
|
{
|
||||||
if ! $(m:G) in $(to-remove)
|
if ! $(m:G) in $(to-remove)
|
||||||
{
|
{
|
||||||
|
local is-variable = [ is-variable-feature $(m:G) ] ;
|
||||||
local att = [ attributes $(m:G) ] ;
|
local att = [ attributes $(m:G) ] ;
|
||||||
if $(m:G) in $(expanded-from-composite) &&
|
if $(m:G) in $(expanded-from-composite) &&
|
||||||
! free in $(att) &&
|
! $(is-variable) &&
|
||||||
! $(m) in $(more)
|
! $(m) in $(more)
|
||||||
{
|
{
|
||||||
import errors ;
|
import errors ;
|
||||||
@@ -1125,7 +1164,7 @@ rule add-defaults ( properties * )
|
|||||||
expanded-from-composite += $(m:G) ;
|
expanded-from-composite += $(m:G) ;
|
||||||
}
|
}
|
||||||
more += $(m) ;
|
more += $(m) ;
|
||||||
if ! subfeature in $(att) && ! free in $(att)
|
if ! subfeature in $(att) && ! $(is-variable)
|
||||||
{
|
{
|
||||||
worklist += $(m) ;
|
worklist += $(m) ;
|
||||||
}
|
}
|
||||||
@@ -1225,6 +1264,7 @@ rule __test__ ( )
|
|||||||
feature variant : debug release profile : implicit composite symmetric ;
|
feature variant : debug release profile : implicit composite symmetric ;
|
||||||
feature stdlib : native stlport ;
|
feature stdlib : native stlport ;
|
||||||
feature magic : : free ;
|
feature magic : : free ;
|
||||||
|
feature version : : version ;
|
||||||
|
|
||||||
compose <variant>debug : <define>_DEBUG <optimization>off ;
|
compose <variant>debug : <define>_DEBUG <optimization>off ;
|
||||||
compose <variant>release : <define>NDEBUG <optimization>on ;
|
compose <variant>release : <define>NDEBUG <optimization>on ;
|
||||||
@@ -1411,6 +1451,13 @@ rule __test__ ( )
|
|||||||
}
|
}
|
||||||
catch \"digital_mars\" is not a known value of feature <toolset> ;
|
catch \"digital_mars\" is not a known value of feature <toolset> ;
|
||||||
|
|
||||||
|
validate-value-string <version> 1.2.3 ;
|
||||||
|
try ;
|
||||||
|
{
|
||||||
|
validate-value-string <version> a.b.c ;
|
||||||
|
}
|
||||||
|
catch \"a.b.c\" is not a valid semantic version for feature \"<version>\" ;
|
||||||
|
|
||||||
try ;
|
try ;
|
||||||
{
|
{
|
||||||
feature foobar : : baz ;
|
feature foobar : : baz ;
|
||||||
@@ -1428,7 +1475,7 @@ rule __test__ ( )
|
|||||||
{
|
{
|
||||||
feature feature2 : : free implicit ;
|
feature feature2 : : free implicit ;
|
||||||
}
|
}
|
||||||
catch free features cannot also be implicit ;
|
catch free features cannot be implicit ;
|
||||||
|
|
||||||
try ;
|
try ;
|
||||||
{
|
{
|
||||||
|
|||||||
+114
-3
@@ -15,6 +15,7 @@ import sequence ;
|
|||||||
import set ;
|
import set ;
|
||||||
import utility ;
|
import utility ;
|
||||||
import args ;
|
import args ;
|
||||||
|
import version ;
|
||||||
|
|
||||||
|
|
||||||
# Refines 'properties' by overriding any non-free and non-conditional properties
|
# Refines 'properties' by overriding any non-free and non-conditional properties
|
||||||
@@ -25,6 +26,7 @@ rule refine ( properties * : requirements * )
|
|||||||
{
|
{
|
||||||
local result ;
|
local result ;
|
||||||
local unset ;
|
local unset ;
|
||||||
|
local to-resolve-version ;
|
||||||
|
|
||||||
# Collect all non-free features in requirements
|
# Collect all non-free features in requirements
|
||||||
for local r in $(requirements)
|
for local r in $(requirements)
|
||||||
@@ -32,7 +34,11 @@ rule refine ( properties * : requirements * )
|
|||||||
# Do not consider conditional requirements.
|
# Do not consider conditional requirements.
|
||||||
if ! [ MATCH "(:<)" : $(r:G=) ] && ! free in [ feature.attributes $(r:G) ]
|
if ! [ MATCH "(:<)" : $(r:G=) ] && ! free in [ feature.attributes $(r:G) ]
|
||||||
{
|
{
|
||||||
if ! $(r) in $(properties)
|
if version in [ feature.attributes $(r:G) ]
|
||||||
|
{
|
||||||
|
to-resolve-version += $(r) ;
|
||||||
|
}
|
||||||
|
else if ! $(r) in $(properties)
|
||||||
{
|
{
|
||||||
# Kill subfeatures of properties that we're changing
|
# Kill subfeatures of properties that we're changing
|
||||||
local sub = [ modules.peek feature : $(r:G).subfeatures ] ;
|
local sub = [ modules.peek feature : $(r:G).subfeatures ] ;
|
||||||
@@ -48,16 +54,49 @@ rule refine ( properties * : requirements * )
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# The version props to resolve get removed from both the props and
|
||||||
|
# requirements. As the resolved version gets added in tot he result.
|
||||||
|
requirements = [ set.difference $(requirements) : $(to-resolve-version) ] ;
|
||||||
|
|
||||||
# Remove properties that are overridden by requirements
|
# Remove properties that are overridden by requirements
|
||||||
for local p in $(properties)
|
for local p in $(properties)
|
||||||
{
|
{
|
||||||
if [ MATCH "(:<)" : $(p:G=) ] || ! $(p:G) in $(unset)
|
if [ MATCH "(:<)" : $(p:G=) ]
|
||||||
{
|
{
|
||||||
|
# Keep conditional properties in result.
|
||||||
|
result += $(p) ;
|
||||||
|
}
|
||||||
|
else if $(p:G) in $(to-resolve-version:G)
|
||||||
|
{
|
||||||
|
# For version feature need to resolve which one is compatible.
|
||||||
|
local resolved = $(p:G=) ;
|
||||||
|
for local r in $(to-resolve-version)
|
||||||
|
{
|
||||||
|
if $(p:G) = $(r:G)
|
||||||
|
{
|
||||||
|
resolved = [ version.semver-resolve $(resolved) $(r:G=) ] $(resolved) ;
|
||||||
|
resolved = $(resolved[1]) ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result += $(p:G)$(resolved) ;
|
||||||
|
}
|
||||||
|
else if ! $(p:G) in $(unset)
|
||||||
|
{
|
||||||
|
# Keep anything that is not overridden, i.e. not in the requirements.
|
||||||
result += $(p) ;
|
result += $(p) ;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return [ sequence.unique $(result) $(requirements) ] ;
|
for local r in $(to-resolve-version)
|
||||||
|
{
|
||||||
|
if ! $(r:G) in $(result:G)
|
||||||
|
{
|
||||||
|
result += $(r) ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
local result = [ sequence.unique $(result) $(requirements) ] ;
|
||||||
|
return $(result) ;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -877,6 +916,61 @@ class property-map
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
rule next-is-match ( current * : next * )
|
||||||
|
{
|
||||||
|
if $(current) in $(next)
|
||||||
|
{
|
||||||
|
return true ;
|
||||||
|
}
|
||||||
|
local current-plain ;
|
||||||
|
local current-version ;
|
||||||
|
local next-plain ;
|
||||||
|
local next-version ;
|
||||||
|
for local c in $(current)
|
||||||
|
{
|
||||||
|
if version in [ feature.attributes $(c:G) ]
|
||||||
|
{
|
||||||
|
current-version += $(c) ;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
current-plain += $(c) ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for local n in $(next)
|
||||||
|
{
|
||||||
|
if version in [ feature.attributes $(n:G) ]
|
||||||
|
{
|
||||||
|
next-version += $(n) ;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
next-plain += $(n) ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ! $(current-plain) in $(next-plain)
|
||||||
|
{
|
||||||
|
return ;
|
||||||
|
}
|
||||||
|
if ! $(current-version:G) in $(next-version:G)
|
||||||
|
{
|
||||||
|
return ;
|
||||||
|
}
|
||||||
|
local next-match ;
|
||||||
|
for local p in $(current-version)
|
||||||
|
{
|
||||||
|
for local q in $(next-version)
|
||||||
|
{
|
||||||
|
if $(p:G) = $(q:G) && ! [ version.semver-resolve $(p:G=) $(q:G=) ]
|
||||||
|
{
|
||||||
|
return ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true ;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
rule __test__ ( )
|
rule __test__ ( )
|
||||||
{
|
{
|
||||||
import assert ;
|
import assert ;
|
||||||
@@ -897,6 +991,7 @@ rule __test__ ( )
|
|||||||
feature.feature optimization : on off ;
|
feature.feature optimization : on off ;
|
||||||
feature.feature variant : debug release : implicit composite symmetric ;
|
feature.feature variant : debug release : implicit composite symmetric ;
|
||||||
feature.feature rtti : on off : link-incompatible ;
|
feature.feature rtti : on off : link-incompatible ;
|
||||||
|
feature.feature version : : version ;
|
||||||
|
|
||||||
feature.compose <variant>debug : <define>_DEBUG <optimization>off ;
|
feature.compose <variant>debug : <define>_DEBUG <optimization>off ;
|
||||||
feature.compose <variant>release : <define>NDEBUG <optimization>on ;
|
feature.compose <variant>release : <define>NDEBUG <optimization>on ;
|
||||||
@@ -931,6 +1026,13 @@ rule __test__ ( )
|
|||||||
: refine <toolset>gcc:<define>foo : <toolset>gcc:<define>bar
|
: refine <toolset>gcc:<define>foo : <toolset>gcc:<define>bar
|
||||||
: $(test-space) ;
|
: $(test-space) ;
|
||||||
|
|
||||||
|
assert.result-set-equal <toolset>gcc <version>2.1
|
||||||
|
: refine <toolset>gcc <version>2.0
|
||||||
|
: <version>2.1 ;
|
||||||
|
assert.result-set-equal <toolset>gcc <version>3.0
|
||||||
|
: refine <toolset>gcc
|
||||||
|
: <version>3.0 ;
|
||||||
|
|
||||||
assert.result
|
assert.result
|
||||||
: evaluate-conditionals-in-context
|
: evaluate-conditionals-in-context
|
||||||
<variant>release,<rtti>off:<define>MY_RELEASE
|
<variant>release,<rtti>off:<define>MY_RELEASE
|
||||||
@@ -980,6 +1082,11 @@ rule __test__ ( )
|
|||||||
validate value : $(test-space) ;
|
validate value : $(test-space) ;
|
||||||
catch \"value\" is not an implicit feature value ;
|
catch \"value\" is not an implicit feature value ;
|
||||||
|
|
||||||
|
validate <version>1.2.3 ;
|
||||||
|
try ;
|
||||||
|
validate <version>a.b.c ;
|
||||||
|
catch \"a.b.c\" is not a valid semantic version for feature \"<version>\" ;
|
||||||
|
|
||||||
assert.result-set-equal <rtti>on
|
assert.result-set-equal <rtti>on
|
||||||
: remove free implicit : <toolset>gcc <define>foo <rtti>on : $(test-space) ;
|
: remove free implicit : <toolset>gcc <define>foo <rtti>on : $(test-space) ;
|
||||||
|
|
||||||
@@ -1027,5 +1134,9 @@ rule __test__ ( )
|
|||||||
: translate <toolset>gcc,!<rtti>off:<define>HELLO
|
: translate <toolset>gcc,!<rtti>off:<define>HELLO
|
||||||
: project-id : project-location : context-module ;
|
: project-id : project-location : context-module ;
|
||||||
|
|
||||||
|
assert.true next-is-match <version>1.1 : <version>1.2 ;
|
||||||
|
assert.false next-is-match <version>1.1 : <version>2.0 ;
|
||||||
|
assert.false next-is-match <version>1.1 : <version>1.0 ;
|
||||||
|
|
||||||
feature.finish-test property-test-temp ;
|
feature.finish-test property-test-temp ;
|
||||||
}
|
}
|
||||||
|
|||||||
+41
-4
@@ -633,6 +633,7 @@ local rule end-building ( main-target-instance )
|
|||||||
#
|
#
|
||||||
class main-target : abstract-target
|
class main-target : abstract-target
|
||||||
{
|
{
|
||||||
|
import property ;
|
||||||
import property-set ;
|
import property-set ;
|
||||||
import sequence ;
|
import sequence ;
|
||||||
import set ;
|
import set ;
|
||||||
@@ -713,6 +714,11 @@ class main-target : abstract-target
|
|||||||
best = $(v) ;
|
best = $(v) ;
|
||||||
best-properties = $(properties) ;
|
best-properties = $(properties) ;
|
||||||
}
|
}
|
||||||
|
else if [ property.next-is-match $(best-properties) : $(properties) ]
|
||||||
|
{
|
||||||
|
best = $(v) ;
|
||||||
|
best-properties = $(properties) ;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
bad = true ;
|
bad = true ;
|
||||||
@@ -1148,6 +1154,7 @@ class basic-target : abstract-target
|
|||||||
import sequence ;
|
import sequence ;
|
||||||
import set ;
|
import set ;
|
||||||
import targets ;
|
import targets ;
|
||||||
|
import version ;
|
||||||
import virtual-target ;
|
import virtual-target ;
|
||||||
|
|
||||||
rule __init__ ( name : project : sources * : requirements * :
|
rule __init__ ( name : project : sources * : requirements * :
|
||||||
@@ -1229,13 +1236,38 @@ class basic-target : abstract-target
|
|||||||
local condition = [ set.difference $(bcondition) : $(ccondition) ] ;
|
local condition = [ set.difference $(bcondition) : $(ccondition) ] ;
|
||||||
condition = [ feature.expand-subfeatures $(condition) : unchecked ] ;
|
condition = [ feature.expand-subfeatures $(condition) : unchecked ] ;
|
||||||
|
|
||||||
if $(condition) in [ $(property-set).raw ]
|
if [ $(property-set).contains-features $(condition:G) ]
|
||||||
{
|
{
|
||||||
if $(debug)
|
# The condition could match. But we need to resolve any features
|
||||||
|
# that use non-equality matching.
|
||||||
|
for local c in $(condition)
|
||||||
{
|
{
|
||||||
ECHO " matched:" $(condition:E=(empty)) ;
|
if version in [ feature.attributes $(c:G) ]
|
||||||
|
{
|
||||||
|
local resolved ;
|
||||||
|
for local v in [ $(property-set).get $(c:G) ]
|
||||||
|
{
|
||||||
|
resolved ?= $(c:G=) ;
|
||||||
|
resolved = [ version.semver-resolve $(v) $(resolved) ] ;
|
||||||
|
if ! $(resolved)
|
||||||
|
{
|
||||||
|
if $(debug)
|
||||||
|
{
|
||||||
|
ECHO " no match:" $(condition:E=(empty)) ;
|
||||||
|
}
|
||||||
|
return no-match ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if ! $(c:G=) in [ $(property-set).get $(c:G) ]
|
||||||
|
{
|
||||||
|
if $(debug)
|
||||||
|
{
|
||||||
|
ECHO " no match:" $(condition:E=(empty)) ;
|
||||||
|
}
|
||||||
|
return no-match ;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return $(condition) ;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -1245,6 +1277,11 @@ class basic-target : abstract-target
|
|||||||
}
|
}
|
||||||
return no-match ;
|
return no-match ;
|
||||||
}
|
}
|
||||||
|
if $(debug)
|
||||||
|
{
|
||||||
|
ECHO " matched:" $(condition:E=(empty)) ;
|
||||||
|
}
|
||||||
|
return $(condition) ;
|
||||||
}
|
}
|
||||||
|
|
||||||
# Takes a target reference, which might be either target id or a dependency
|
# Takes a target reference, which might be either target id or a dependency
|
||||||
|
|||||||
@@ -194,5 +194,12 @@ rule __test__ ( )
|
|||||||
assert.true version-compatible 04 : 4 ;
|
assert.true version-compatible 04 : 4 ;
|
||||||
assert.true version-compatible 04 00 : 04 ;
|
assert.true version-compatible 04 00 : 04 ;
|
||||||
assert.true version-compatible 04 : 04 00 ;
|
assert.true version-compatible 04 : 04 00 ;
|
||||||
|
|
||||||
|
assert.true semver-verify 1.2.3 ;
|
||||||
|
assert.false semver-verify a.b.c ;
|
||||||
|
assert.false semver-verify "1.2.3 extra" ;
|
||||||
|
|
||||||
|
assert.true semver-resolve 1.2 1.3 ;
|
||||||
|
assert.false semver-resolve 1.3 1.2 ;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,964 @@
|
|||||||
|
// Copyright René Ferdinand Rivera Morell
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See
|
||||||
|
// versioned_narrowc_accumulateompanying file LICENSE.txt or copy at
|
||||||
|
// http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
|
||||||
|
#ifndef VERSIONED_VERSIONED_HPP
|
||||||
|
#define VERSIONED_VERSIONED_HPP
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <limits>
|
||||||
|
#include <system_error>
|
||||||
|
#include <tuple>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#if defined(__cpp_lib_to_chars)
|
||||||
|
# include <charconv>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define VERSIONED_VERSION_MAJOR 0
|
||||||
|
#define VERSIONED_VERSION_MINOR 1
|
||||||
|
#define VERSIONED_VERSION_PATCH 0
|
||||||
|
|
||||||
|
#define VERSIONED_VERSION \
|
||||||
|
(((VERSIONED_VERSION_MAJOR) * 10000000) \
|
||||||
|
+ ((VERSIONED_VERSION_MINOR) * 100000) + (VERSIONED_VERSION_PATCH))
|
||||||
|
|
||||||
|
// Some platform define inconvenient macros.
|
||||||
|
#ifdef major
|
||||||
|
# undef major
|
||||||
|
#endif
|
||||||
|
#ifdef minor
|
||||||
|
# undef minor
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace bfg { namespace versioned { namespace detail {
|
||||||
|
|
||||||
|
// The decimal digits.
|
||||||
|
inline constexpr bool is_digit(const char c) { return c >= '0' && c <= '9'; }
|
||||||
|
|
||||||
|
// Alphanumeric, for identifiers.
|
||||||
|
inline constexpr bool is_alphanum(const char c)
|
||||||
|
{
|
||||||
|
return is_digit(c) || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Identifier allowed character.
|
||||||
|
inline constexpr bool is_ident(const char c)
|
||||||
|
{
|
||||||
|
return is_alphanum(c) || (c == '-');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Base 10 only positive integer parsing. Believe it or not, this rather
|
||||||
|
// simple code provides the fastest throughput for parsing sequential
|
||||||
|
// integers.
|
||||||
|
template <typename I>
|
||||||
|
const char * from_chars_10(const char * first, const char * last, I & value)
|
||||||
|
{
|
||||||
|
I e = 0;
|
||||||
|
while (first < last && is_digit(*first)) e = e * 10 + (*(first++) - '0');
|
||||||
|
value = e;
|
||||||
|
return first;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare, as ASCII, two string ranges.
|
||||||
|
inline int chars_cmp(
|
||||||
|
const char * a0, const char * an, const char * b0, const char * bn)
|
||||||
|
{
|
||||||
|
while ((a0 < an) && (b0 < bn) && (*a0 == *b0))
|
||||||
|
{
|
||||||
|
a0 += 1;
|
||||||
|
b0 += 1;
|
||||||
|
}
|
||||||
|
return *a0 - *b0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename N, typename U>
|
||||||
|
constexpr N masked_max(U value)
|
||||||
|
{
|
||||||
|
return value & static_cast<U>(::std::numeric_limits<N>::max());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simple hash values combine.
|
||||||
|
template <class T, class... N>
|
||||||
|
T hash_combine(T seed, N... an)
|
||||||
|
{
|
||||||
|
T args[] = { an... };
|
||||||
|
// Init.
|
||||||
|
T result = seed;
|
||||||
|
// Combine.
|
||||||
|
for (auto a : args)
|
||||||
|
{
|
||||||
|
result ^= a + masked_max<T>(3772387269305686495)
|
||||||
|
+ (result << (30 * 2 / sizeof(T)))
|
||||||
|
+ (result >> (13 * 2 / sizeof(T)));
|
||||||
|
}
|
||||||
|
// Mix.
|
||||||
|
result ^= (result >> (16 * 2 / sizeof(T)));
|
||||||
|
result *= masked_max<T>(448100074733706);
|
||||||
|
result ^= (result << (8 * 2 / sizeof(T)));
|
||||||
|
result *= masked_max<T>(190056597654806);
|
||||||
|
result ^= (result >> (11 * 2 / sizeof(T)));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Backfill to_string as it's missing on some runtimes.
|
||||||
|
inline ::std::string to_string_10(::std::size_t value)
|
||||||
|
{
|
||||||
|
::std::string result;
|
||||||
|
result.reserve(::std::numeric_limits<::std::size_t>::max_digits10);
|
||||||
|
do
|
||||||
|
{
|
||||||
|
result += '0' + (value % 10);
|
||||||
|
value /= 10;
|
||||||
|
}
|
||||||
|
while (value > 0);
|
||||||
|
::std::reverse(result.begin(), result.end());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}}} // namespace bfg::versioned::detail
|
||||||
|
|
||||||
|
namespace bfg { namespace versioned {
|
||||||
|
struct from_chars_result
|
||||||
|
{
|
||||||
|
const char * ptr;
|
||||||
|
::std::errc ec;
|
||||||
|
|
||||||
|
constexpr explicit operator bool() const noexcept
|
||||||
|
{
|
||||||
|
return ec == ::std::errc { };
|
||||||
|
}
|
||||||
|
|
||||||
|
friend constexpr bool operator==(
|
||||||
|
const from_chars_result & a, const from_chars_result & b)
|
||||||
|
{
|
||||||
|
return (a.ptr == b.ptr) && (a.ec == b.ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(__cpp_lib_to_chars) && (__cpp_lib_to_chars >= 201611L)
|
||||||
|
constexpr operator std::from_chars_result() { return { ptr, ec }; }
|
||||||
|
constexpr from_chars_result & operator=(
|
||||||
|
const std::from_chars_result & other) noexcept
|
||||||
|
{
|
||||||
|
return *this = from_chars_result { other.ptr, other.ec };
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
}} // namespace bfg::versioned
|
||||||
|
|
||||||
|
// tag::version_core-sym[]
|
||||||
|
namespace bfg { namespace versioned {
|
||||||
|
template <class Number, ::std::size_t Count = 3>
|
||||||
|
class version_core
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// types
|
||||||
|
using element_t = Number;
|
||||||
|
static constexpr ::std::size_t element_c = Count;
|
||||||
|
|
||||||
|
// construction, destruction, and assignment
|
||||||
|
version_core() = default;
|
||||||
|
version_core(version_core &&) = default;
|
||||||
|
version_core(const version_core &) = default;
|
||||||
|
version_core & operator=(const version_core &) = default;
|
||||||
|
template <class... I>
|
||||||
|
version_core(element_t a0, I... an);
|
||||||
|
|
||||||
|
// observers
|
||||||
|
element_t & at(::std::size_t i);
|
||||||
|
const element_t & at(::std::size_t i) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static_assert(::std::is_integral<Number>::value,
|
||||||
|
"Version element type is not an integral type."); // exposition only
|
||||||
|
static_assert(Count > 0,
|
||||||
|
"Version must contain at least one part."); // exposition only
|
||||||
|
|
||||||
|
element_t number[element_c] = { }; // exposition only
|
||||||
|
// end::version_core-sym[]
|
||||||
|
|
||||||
|
template <class N, ::std::size_t C>
|
||||||
|
friend from_chars_result from_chars(
|
||||||
|
const char * first, const char * last, version_core<N, C> & value);
|
||||||
|
|
||||||
|
template <class N, ::std::size_t C>
|
||||||
|
friend ::std::string to_string(const version_core<N, C> & value);
|
||||||
|
|
||||||
|
template <class N0, ::std::size_t C0, class N1, ::std::size_t C1>
|
||||||
|
friend int compare(
|
||||||
|
const version_core<N0, C0> & a, const version_core<N1, C1> & b);
|
||||||
|
|
||||||
|
template <class N0, ::std::size_t C0, class N1, ::std::size_t C1>
|
||||||
|
friend bool operator==(
|
||||||
|
const version_core<N0, C0> & a, const version_core<N1, C1> & b);
|
||||||
|
|
||||||
|
template <class N0, ::std::size_t C0, class N1, ::std::size_t C1>
|
||||||
|
friend bool operator<(
|
||||||
|
const version_core<N0, C0> & a, const version_core<N1, C1> & b);
|
||||||
|
|
||||||
|
template <class N, ::std::size_t C>
|
||||||
|
friend ::std::size_t hash(const version_core<N, C> & value);
|
||||||
|
// tag::version_core-sym[]
|
||||||
|
};
|
||||||
|
}} // namespace bfg::versioned
|
||||||
|
// end::version_core-sym[]
|
||||||
|
|
||||||
|
namespace bfg { namespace versioned {
|
||||||
|
template <class N, ::std::size_t C>
|
||||||
|
template <class... I>
|
||||||
|
version_core<N, C>::version_core(element_t a0, I... an)
|
||||||
|
{
|
||||||
|
element_t args[] = { a0, an... };
|
||||||
|
::std::size_t i = 0;
|
||||||
|
for (auto a : args) number[i++] = a;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class N, ::std::size_t C>
|
||||||
|
typename version_core<N, C>::element_t & version_core<N, C>::at(::std::size_t i)
|
||||||
|
{
|
||||||
|
if (i >= element_c)
|
||||||
|
throw ::std::out_of_range(
|
||||||
|
"No such component: " + detail::to_string_10(i));
|
||||||
|
return number[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class N, ::std::size_t C>
|
||||||
|
const typename version_core<N, C>::element_t & version_core<N, C>::at(
|
||||||
|
::std::size_t i) const
|
||||||
|
{
|
||||||
|
if (i >= element_c)
|
||||||
|
throw ::std::out_of_range(
|
||||||
|
"No such component: " + detail::to_string_10(i));
|
||||||
|
return number[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class N, ::std::size_t C>
|
||||||
|
from_chars_result from_chars(
|
||||||
|
const char * first, const char * last, version_core<N, C> & value)
|
||||||
|
{
|
||||||
|
if (first == last || !first || !last)
|
||||||
|
return from_chars_result { first, ::std::errc::invalid_argument };
|
||||||
|
|
||||||
|
from_chars_result result { first, ::std::errc(0) };
|
||||||
|
typename version_core<N, C>::element_t
|
||||||
|
number[version_core<N, C>::element_c] { };
|
||||||
|
|
||||||
|
for (::std::size_t i = 0; i < version_core<N, C>::element_c; ++i)
|
||||||
|
{
|
||||||
|
result.ptr = detail::from_chars_10(result.ptr, last, number[i]);
|
||||||
|
if (result.ptr == last || *result.ptr != '.') break;
|
||||||
|
result.ptr += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (::std::size_t i = 0; i < version_core<N, C>::element_c; ++i)
|
||||||
|
value.number[i] = number[i];
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class N, ::std::size_t C>
|
||||||
|
::std::string to_string(const version_core<N, C> & value)
|
||||||
|
{
|
||||||
|
::std::string result;
|
||||||
|
for (::std::size_t i = version_core<N, C>::element_c; i > 0; --i)
|
||||||
|
{
|
||||||
|
::std::size_t n = i - 1;
|
||||||
|
if (n == 0)
|
||||||
|
result = detail::to_string_10(value.number[n]) + result;
|
||||||
|
else if (value.number[n] > 0)
|
||||||
|
result = "." + detail::to_string_10(value.number[n]) + result;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class N0, ::std::size_t C0, class N1, ::std::size_t C1>
|
||||||
|
int compare(const version_core<N0, C0> & a, const version_core<N1, C1> & b)
|
||||||
|
{
|
||||||
|
for (::std::size_t i = 0; i < version_core<N0, C0>::element_c
|
||||||
|
&& i < version_core<N1, C1>::element_c;
|
||||||
|
++i)
|
||||||
|
{
|
||||||
|
auto c = a.number[i]
|
||||||
|
- static_cast<typename version_core<N0, C0>::element_t>(
|
||||||
|
b.number[i]);
|
||||||
|
if (c != 0) return int(c);
|
||||||
|
}
|
||||||
|
return int(version_core<N0, C0>::element_c)
|
||||||
|
- int(version_core<N1, C1>::element_c);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class N0, ::std::size_t C0, class N1, ::std::size_t C1>
|
||||||
|
bool operator==(const version_core<N0, C0> & a, const version_core<N1, C1> & b)
|
||||||
|
{
|
||||||
|
return compare(a, b) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class N0, ::std::size_t C0, class N1, ::std::size_t C1>
|
||||||
|
bool operator<(const version_core<N0, C0> & a, const version_core<N1, C1> & b)
|
||||||
|
{
|
||||||
|
return compare(a, b) < 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class N, ::std::size_t C>
|
||||||
|
::std::size_t hash(const version_core<N, C> & value)
|
||||||
|
{
|
||||||
|
::std::hash<typename version_core<N, C>::element_t> h { };
|
||||||
|
::std::size_t result = 0;
|
||||||
|
for (auto n : value.number) result = detail::hash_combine(result, h(n));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <size_t I, class N, ::std::size_t C>
|
||||||
|
const typename version_core<N, C>::element_t & get(
|
||||||
|
const version_core<N, C> & value)
|
||||||
|
{
|
||||||
|
return value.at(I);
|
||||||
|
}
|
||||||
|
}} // namespace bfg::versioned
|
||||||
|
|
||||||
|
//
|
||||||
|
namespace std {
|
||||||
|
template <class N, ::std::size_t C>
|
||||||
|
struct hash<::bfg::versioned::version_core<N, C>>
|
||||||
|
{
|
||||||
|
::std::size_t operator()(
|
||||||
|
const ::bfg::versioned::version_core<N, C> & value) const noexcept
|
||||||
|
{
|
||||||
|
return ::bfg::versioned::hash(value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#if defined(__clang__)
|
||||||
|
# pragma clang diagnostic push
|
||||||
|
# pragma clang diagnostic ignored "-Wmismatched-tags"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template <class N, ::std::size_t C>
|
||||||
|
struct tuple_size<::bfg::versioned::version_core<N, C>>
|
||||||
|
: integral_constant<::std::size_t,
|
||||||
|
::bfg::versioned::version_core<N, C>::element_c>
|
||||||
|
{ };
|
||||||
|
|
||||||
|
template <size_t I, class N, ::std::size_t C>
|
||||||
|
struct tuple_element<I, ::bfg::versioned::version_core<N, C>>
|
||||||
|
{
|
||||||
|
using type = const typename ::bfg::versioned::version_core<N, C>::element_t;
|
||||||
|
};
|
||||||
|
|
||||||
|
#if defined(__clang__)
|
||||||
|
# pragma clang diagnostic pop
|
||||||
|
#endif
|
||||||
|
} // namespace std
|
||||||
|
|
||||||
|
// tag::version_tag-syn[]
|
||||||
|
namespace bfg { namespace versioned {
|
||||||
|
class version_tag
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// types
|
||||||
|
using element_t = ::std::string;
|
||||||
|
using range_element_t = ::std::tuple<const char *, const char *>;
|
||||||
|
|
||||||
|
// construction, destruction, and assignment
|
||||||
|
version_tag() = default;
|
||||||
|
version_tag(version_tag &&) = default;
|
||||||
|
version_tag(const version_tag &) = default;
|
||||||
|
version_tag & operator=(const version_tag &) = default;
|
||||||
|
template <class... S>
|
||||||
|
version_tag(const char * a0, S... an);
|
||||||
|
template <class... S>
|
||||||
|
version_tag(const ::std::string & a0, S... an);
|
||||||
|
|
||||||
|
// observers
|
||||||
|
element_t at(::std::size_t i) const;
|
||||||
|
range_element_t range_at(::std::size_t i) const;
|
||||||
|
bool empty() const;
|
||||||
|
::std::size_t size() const;
|
||||||
|
// end::version_tag-syn[]
|
||||||
|
|
||||||
|
private:
|
||||||
|
// The info contains the part that is parsed from any original data.
|
||||||
|
::std::string info_;
|
||||||
|
// The parts hold the index to the end of each parsed segment.
|
||||||
|
::std::vector<::std::size_t> parts_;
|
||||||
|
|
||||||
|
friend from_chars_result from_chars(
|
||||||
|
const char * first, const char * last, version_tag & value);
|
||||||
|
|
||||||
|
friend ::std::string to_string(const version_tag & value);
|
||||||
|
|
||||||
|
friend int compare(const version_tag & a, const version_tag & b);
|
||||||
|
|
||||||
|
friend bool operator==(const version_tag & a, const version_tag & b);
|
||||||
|
friend bool operator<(const version_tag & a, const version_tag & b);
|
||||||
|
|
||||||
|
friend ::std::size_t hash(const version_tag & value);
|
||||||
|
// tag::version_tag-syn[]
|
||||||
|
};
|
||||||
|
}} // namespace bfg::versioned
|
||||||
|
// end::version_tag-syn[]
|
||||||
|
|
||||||
|
namespace bfg { namespace versioned {
|
||||||
|
template <class... S>
|
||||||
|
version_tag::version_tag(const char * a0, S... an)
|
||||||
|
{
|
||||||
|
info_ += a0;
|
||||||
|
parts_.emplace_back(info_.size());
|
||||||
|
const char * args[] = { an... };
|
||||||
|
for (auto a : args)
|
||||||
|
{
|
||||||
|
info_ += ".";
|
||||||
|
info_ += a;
|
||||||
|
parts_.emplace_back(info_.size());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class... S>
|
||||||
|
version_tag::version_tag(const ::std::string & a0, S... an)
|
||||||
|
{
|
||||||
|
info_ += a0;
|
||||||
|
parts_.emplace_back(info_.size());
|
||||||
|
const char * args[] = { an... };
|
||||||
|
for (auto a : args)
|
||||||
|
{
|
||||||
|
info_ += ".";
|
||||||
|
info_ += a;
|
||||||
|
parts_.emplace_back(info_.size());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline version_tag::element_t version_tag::at(::std::size_t i) const
|
||||||
|
{
|
||||||
|
auto r = this->range_at(i);
|
||||||
|
return ::std::string(::std::get<0>(r), ::std::get<1>(r));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline version_tag::range_element_t version_tag::range_at(::std::size_t i) const
|
||||||
|
{
|
||||||
|
if (i >= parts_.size())
|
||||||
|
throw ::std::out_of_range(
|
||||||
|
"No such component: " + detail::to_string_10(i));
|
||||||
|
return ::std::make_tuple(info_.c_str() + (i == 0 ? 0 : parts_[i - 1] + 1),
|
||||||
|
info_.c_str() + parts_[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool version_tag::empty() const { return info_.empty(); }
|
||||||
|
|
||||||
|
inline ::std::size_t version_tag::size() const { return parts_.size(); }
|
||||||
|
|
||||||
|
inline from_chars_result from_chars(
|
||||||
|
const char * first, const char * last, version_tag & value)
|
||||||
|
{
|
||||||
|
if (first == last)
|
||||||
|
return from_chars_result { first, ::std::errc::invalid_argument };
|
||||||
|
|
||||||
|
from_chars_result result { };
|
||||||
|
::std::vector<::std::size_t> parts;
|
||||||
|
|
||||||
|
for (auto begin = first; begin < last;)
|
||||||
|
{
|
||||||
|
auto end = ::std::find_if(begin, last,
|
||||||
|
[](char c) { return c == '.' || !detail::is_ident(c); });
|
||||||
|
if (end == first)
|
||||||
|
{
|
||||||
|
// No valid info parts found. Indicate an error, as nothing is
|
||||||
|
// not an allowed input.
|
||||||
|
result.ptr = end;
|
||||||
|
result.ec = ::std::errc::invalid_argument;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (end == last || *end == '.')
|
||||||
|
{
|
||||||
|
// Found the EOS or a part separator. We have a part. And may
|
||||||
|
// have more.
|
||||||
|
parts.emplace_back(end - first);
|
||||||
|
begin = end + 1;
|
||||||
|
}
|
||||||
|
else if (!detail::is_ident(*end))
|
||||||
|
{
|
||||||
|
// We parsed a valid part, but reached the end of the valid
|
||||||
|
// characters.
|
||||||
|
parts.emplace_back(end - first);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Should never happen. But check, and report an error if it
|
||||||
|
// does.
|
||||||
|
result.ptr = nullptr;
|
||||||
|
result.ec = ::std::errc::invalid_argument;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.ec == ::std::errc { })
|
||||||
|
{
|
||||||
|
result.ptr = first + parts.back();
|
||||||
|
value.info_ = ::std::string(first, result.ptr);
|
||||||
|
value.parts_ = parts;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline ::std::string to_string(const version_tag & value)
|
||||||
|
{
|
||||||
|
return value.info_;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int compare(const version_tag & a, const version_tag & b)
|
||||||
|
{
|
||||||
|
if (!a.empty() && b.empty()) return -1;
|
||||||
|
if (a.empty() && !b.empty()) return 1;
|
||||||
|
for (::std::size_t i = 0; i < a.size() && i < b.size(); ++i)
|
||||||
|
{
|
||||||
|
auto a_chars = a.range_at(i);
|
||||||
|
auto b_chars = b.range_at(i);
|
||||||
|
auto c
|
||||||
|
= detail::chars_cmp(::std::get<0>(a_chars), ::std::get<1>(a_chars),
|
||||||
|
::std::get<0>(b_chars), ::std::get<1>(b_chars));
|
||||||
|
if (c != 0) return c;
|
||||||
|
}
|
||||||
|
return int(a.size()) - int(b.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool operator==(const version_tag & a, const version_tag & b)
|
||||||
|
{
|
||||||
|
return compare(a, b) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool operator<(const version_tag & a, const version_tag & b)
|
||||||
|
{
|
||||||
|
return compare(a, b) < 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline ::std::size_t hash(const version_tag & value)
|
||||||
|
{
|
||||||
|
return ::std::hash<decltype(value.info_)> { }(value.info_);
|
||||||
|
}
|
||||||
|
}} // namespace bfg::versioned
|
||||||
|
|
||||||
|
namespace std {
|
||||||
|
template <>
|
||||||
|
struct hash<::bfg::versioned::version_tag>
|
||||||
|
{
|
||||||
|
::std::size_t operator()(
|
||||||
|
const ::bfg::versioned::version_tag & value) const noexcept
|
||||||
|
{
|
||||||
|
return ::bfg::versioned::hash(value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace std
|
||||||
|
|
||||||
|
// tag::prerelease_version-syn[]
|
||||||
|
namespace bfg { namespace versioned {
|
||||||
|
template <class Number>
|
||||||
|
class prerelease_version : public version_tag
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// types
|
||||||
|
using number_element_t = Number;
|
||||||
|
|
||||||
|
// construction, destruction, and assignment
|
||||||
|
using version_tag::version_tag;
|
||||||
|
|
||||||
|
// observers
|
||||||
|
number_element_t number_at(::std::size_t i) const;
|
||||||
|
bool is_number_at(::std::size_t i) const;
|
||||||
|
// end::prerelease_version-syn[]
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool is_number_at(::std::size_t i, number_element_t & n) const
|
||||||
|
{
|
||||||
|
auto r = this->range_at(i);
|
||||||
|
const char * e
|
||||||
|
= detail::from_chars_10(::std::get<0>(r), ::std::get<1>(r), n);
|
||||||
|
return (e == ::std::get<1>(r));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class N0, class N1>
|
||||||
|
friend int compare(
|
||||||
|
const prerelease_version<N0> & a, const prerelease_version<N1> & b);
|
||||||
|
|
||||||
|
template <class N0, class N1>
|
||||||
|
friend bool operator==(
|
||||||
|
const prerelease_version<N0> & a, const prerelease_version<N1> & b);
|
||||||
|
|
||||||
|
template <class N0, class N1>
|
||||||
|
friend bool operator<(
|
||||||
|
const prerelease_version<N0> & a, const prerelease_version<N1> & b);
|
||||||
|
|
||||||
|
template <class N>
|
||||||
|
::std::size_t hash(const prerelease_version<N> & value);
|
||||||
|
// tag::prerelease_version-syn[]
|
||||||
|
};
|
||||||
|
}} // namespace bfg::versioned
|
||||||
|
// end::prerelease_version-syn[]
|
||||||
|
|
||||||
|
namespace bfg { namespace versioned {
|
||||||
|
template <class N>
|
||||||
|
typename prerelease_version<N>::number_element_t prerelease_version<
|
||||||
|
N>::number_at(::std::size_t i) const
|
||||||
|
{
|
||||||
|
number_element_t n { };
|
||||||
|
if (!is_number_at(i, n))
|
||||||
|
throw ::std::invalid_argument("Element is not a number.");
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class N>
|
||||||
|
bool prerelease_version<N>::is_number_at(::std::size_t i) const
|
||||||
|
{
|
||||||
|
number_element_t n { };
|
||||||
|
return is_number_at(i, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class N0, class N1>
|
||||||
|
int compare(const prerelease_version<N0> & a, const prerelease_version<N1> & b)
|
||||||
|
{
|
||||||
|
if (!a.empty() && b.empty()) return -1;
|
||||||
|
if (a.empty() && !b.empty()) return 1;
|
||||||
|
for (::std::size_t i = 0; i < a.size() && i < b.size(); ++i)
|
||||||
|
{
|
||||||
|
typename prerelease_version<N0>::number_element_t a_n { };
|
||||||
|
bool a_is_num = a.is_number_at(i, a_n);
|
||||||
|
typename prerelease_version<N1>::number_element_t b_n { };
|
||||||
|
bool b_is_num = b.is_number_at(i, b_n);
|
||||||
|
if (a_is_num && b_is_num)
|
||||||
|
{
|
||||||
|
auto b_e = static_cast<
|
||||||
|
typename prerelease_version<N0>::number_element_t>(b_n);
|
||||||
|
if (a_n < b_e) return -1;
|
||||||
|
if (b_e < a_n) return 1;
|
||||||
|
}
|
||||||
|
else if (a_is_num)
|
||||||
|
return -1;
|
||||||
|
else if (b_is_num)
|
||||||
|
return 1;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto a_chars = a.range_at(i);
|
||||||
|
auto b_chars = b.range_at(i);
|
||||||
|
auto c = detail::chars_cmp(::std::get<0>(a_chars),
|
||||||
|
::std::get<1>(a_chars), ::std::get<0>(b_chars),
|
||||||
|
::std::get<1>(b_chars));
|
||||||
|
if (c != 0) return c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return int(a.size()) - int(b.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class N0, class N1>
|
||||||
|
bool operator==(
|
||||||
|
const prerelease_version<N0> & a, const prerelease_version<N1> & b)
|
||||||
|
{
|
||||||
|
return compare(a, b) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class N0, class N1>
|
||||||
|
bool operator<(
|
||||||
|
const prerelease_version<N0> & a, const prerelease_version<N1> & b)
|
||||||
|
{
|
||||||
|
return compare(a, b) < 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class N>
|
||||||
|
::std::size_t hash(const prerelease_version<N> & value)
|
||||||
|
{
|
||||||
|
return hash(static_cast<const version_tag &>(value));
|
||||||
|
}
|
||||||
|
}} // namespace bfg::versioned
|
||||||
|
|
||||||
|
namespace std {
|
||||||
|
template <class N>
|
||||||
|
struct hash<::bfg::versioned::prerelease_version<N>>
|
||||||
|
{
|
||||||
|
::std::size_t operator()(
|
||||||
|
const ::bfg::versioned::prerelease_version<N> & value) const noexcept
|
||||||
|
{
|
||||||
|
return ::bfg::versioned::hash(value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace std
|
||||||
|
|
||||||
|
// tag::build_metadata-syn[]
|
||||||
|
namespace bfg { namespace versioned {
|
||||||
|
class build_metadata : public version_tag
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// construction, destruction, and assignment
|
||||||
|
using version_tag::version_tag;
|
||||||
|
};
|
||||||
|
}} // namespace bfg::versioned
|
||||||
|
// end::build_metadata-syn[]
|
||||||
|
|
||||||
|
// tag::semver-syn[]
|
||||||
|
namespace bfg { namespace versioned {
|
||||||
|
template <class Number = int,
|
||||||
|
class Prerelease = prerelease_version<Number>,
|
||||||
|
class Build = build_metadata>
|
||||||
|
class semver
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// types
|
||||||
|
using version_t = version_core<Number, 3>;
|
||||||
|
using prerelease_t = Prerelease;
|
||||||
|
using build_t = Build;
|
||||||
|
|
||||||
|
// construction, destruction, and assignment
|
||||||
|
semver() = default;
|
||||||
|
semver(semver &&) = default;
|
||||||
|
semver(const semver &) = default;
|
||||||
|
semver(version_t, prerelease_t = { }, build_t = { });
|
||||||
|
semver & operator=(semver &&) = default;
|
||||||
|
|
||||||
|
// observers
|
||||||
|
const version_t & version() const;
|
||||||
|
version_t & version();
|
||||||
|
const prerelease_t & prerelease() const;
|
||||||
|
prerelease_t & prerelease();
|
||||||
|
const build_t & build() const;
|
||||||
|
build_t & build();
|
||||||
|
typename version_t::element_t major() const;
|
||||||
|
typename version_t::element_t minor() const;
|
||||||
|
typename version_t::element_t patch() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
version_t core_; // exposition only
|
||||||
|
prerelease_t pre_; // exposition only
|
||||||
|
build_t build_; // exposition only
|
||||||
|
// end::semver-syn[]
|
||||||
|
|
||||||
|
template <class N, class P, class B>
|
||||||
|
friend from_chars_result from_chars(
|
||||||
|
const char * first, const char * last, semver<N, P, B> & value);
|
||||||
|
|
||||||
|
template <class N, class P, class B>
|
||||||
|
friend ::std::string to_string(const semver<N, P, B> & value);
|
||||||
|
|
||||||
|
template <class N0, class P0, class B0, class N1, class P1, class B1>
|
||||||
|
friend int compare(
|
||||||
|
const semver<N0, P0, B0> & a, const semver<N1, P1, B1> & b);
|
||||||
|
|
||||||
|
template <class N0, class P0, class B0, class N1, class P1, class B1>
|
||||||
|
friend bool operator==(
|
||||||
|
const semver<N0, P0, B0> & a, const semver<N1, P1, B1> & b);
|
||||||
|
|
||||||
|
template <class N0, class P0, class B0, class N1, class P1, class B1>
|
||||||
|
friend bool operator<(
|
||||||
|
const semver<N0, P0, B0> & a, const semver<N1, P1, B1> & b);
|
||||||
|
|
||||||
|
template <class N, class P, class B>
|
||||||
|
friend ::std::size_t hash(const semver<N, P, B> & value);
|
||||||
|
// tag::semver-syn[]
|
||||||
|
};
|
||||||
|
}} // namespace bfg::versioned
|
||||||
|
// end::semver-syn[]
|
||||||
|
|
||||||
|
namespace bfg { namespace versioned {
|
||||||
|
|
||||||
|
template <class N, class P, class B>
|
||||||
|
semver<N, P, B>::semver(version_t v, prerelease_t p, build_t b)
|
||||||
|
: core_(v)
|
||||||
|
, pre_(p)
|
||||||
|
, build_(b)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
template <class N, class P, class B>
|
||||||
|
const typename semver<N, P, B>::version_t & semver<N, P, B>::version() const
|
||||||
|
{
|
||||||
|
return core_;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class N, class P, class B>
|
||||||
|
typename semver<N, P, B>::version_t & semver<N, P, B>::version()
|
||||||
|
{
|
||||||
|
return core_;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class N, class P, class B>
|
||||||
|
const typename semver<N, P, B>::prerelease_t & semver<N, P, B>::prerelease()
|
||||||
|
const
|
||||||
|
{
|
||||||
|
return pre_;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class N, class P, class B>
|
||||||
|
typename semver<N, P, B>::prerelease_t & semver<N, P, B>::prerelease()
|
||||||
|
{
|
||||||
|
return pre_;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class N, class P, class B>
|
||||||
|
const typename semver<N, P, B>::build_t & semver<N, P, B>::build() const
|
||||||
|
{
|
||||||
|
return build_;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class N, class P, class B>
|
||||||
|
typename semver<N, P, B>::build_t & semver<N, P, B>::build()
|
||||||
|
{
|
||||||
|
return build_;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class N, class P, class B>
|
||||||
|
typename semver<N, P, B>::version_t::element_t semver<N, P, B>::major() const
|
||||||
|
{
|
||||||
|
return core_.at(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class N, class P, class B>
|
||||||
|
typename semver<N, P, B>::version_t::element_t semver<N, P, B>::minor() const
|
||||||
|
{
|
||||||
|
return core_.at(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class N, class P, class B>
|
||||||
|
typename semver<N, P, B>::version_t::element_t semver<N, P, B>::patch() const
|
||||||
|
{
|
||||||
|
return core_.at(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class N, class P, class B>
|
||||||
|
from_chars_result from_chars(
|
||||||
|
const char * first, const char * last, semver<N, P, B> & value)
|
||||||
|
{
|
||||||
|
if (first == last)
|
||||||
|
return from_chars_result { first, ::std::errc::invalid_argument };
|
||||||
|
|
||||||
|
from_chars_result result { };
|
||||||
|
|
||||||
|
typename semver<N, P, B>::version_t v;
|
||||||
|
{
|
||||||
|
result = from_chars(first, last, v);
|
||||||
|
if (result.ec != ::std::errc { }) return result;
|
||||||
|
first = result.ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
typename semver<N, P, B>::prerelease_t p;
|
||||||
|
if (first != last && *first == '-')
|
||||||
|
{
|
||||||
|
result = from_chars(first + 1, last, p);
|
||||||
|
if (result.ec != ::std::errc { }) return result;
|
||||||
|
first = result.ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
typename semver<N, P, B>::build_t b;
|
||||||
|
if (first != last && *first == '+')
|
||||||
|
{
|
||||||
|
result = from_chars(first + 1, last, b);
|
||||||
|
if (result.ec != ::std::errc { }) return result;
|
||||||
|
first = result.ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
value.core_ = v;
|
||||||
|
value.pre_ = p;
|
||||||
|
value.build_ = b;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class N, class P, class B>
|
||||||
|
::std::string to_string(const semver<N, P, B> & value)
|
||||||
|
{
|
||||||
|
::std::string result = to_string(value.core_);
|
||||||
|
if (!value.pre_.empty()) result += "-" + to_string(value.pre_);
|
||||||
|
if (!value.build_.empty()) result += "+" + to_string(value.build_);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class N0, class P0, class B0, class N1, class P1, class B1>
|
||||||
|
int compare(const semver<N0, P0, B0> & a, const semver<N1, P1, B1> & b)
|
||||||
|
{
|
||||||
|
auto x = compare(a.core_, b.core_);
|
||||||
|
if (x == 0) return compare(a.pre_, b.pre_);
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class N0, class P0, class B0, class N1, class P1, class B1>
|
||||||
|
bool operator==(const semver<N0, P0, B0> & a, const semver<N1, P1, B1> & b)
|
||||||
|
{
|
||||||
|
return compare(a, b) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class N0, class P0, class B0, class N1, class P1, class B1>
|
||||||
|
bool operator<(const semver<N0, P0, B0> & a, const semver<N1, P1, B1> & b)
|
||||||
|
{
|
||||||
|
return compare(a, b) < 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class N, class P, class B>
|
||||||
|
::std::size_t hash(const semver<N, P, B> & value)
|
||||||
|
{
|
||||||
|
return detail::hash_combine(::bfg::versioned::hash(value.core_),
|
||||||
|
::bfg::versioned::hash(value.pre_),
|
||||||
|
::bfg::versioned::hash(value.build_));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <::std::size_t I, class N, class P, class B>
|
||||||
|
typename ::std::enable_if<I == 0,
|
||||||
|
const typename ::std::tuple_element<I, semver<N, P, B>>::type>::type &
|
||||||
|
get(const semver<N, P, B> & value)
|
||||||
|
{
|
||||||
|
return value.version();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <::std::size_t I, class N, class P, class B>
|
||||||
|
typename ::std::enable_if<I == 1,
|
||||||
|
const typename ::std::tuple_element<I, semver<N, P, B>>::type>::type &
|
||||||
|
get(const semver<N, P, B> & value)
|
||||||
|
{
|
||||||
|
return value.prerelease();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <::std::size_t I, class N, class P, class B>
|
||||||
|
typename ::std::enable_if<I == 2,
|
||||||
|
const typename ::std::tuple_element<I, semver<N, P, B>>::type>::type &
|
||||||
|
get(const semver<N, P, B> & value)
|
||||||
|
{
|
||||||
|
return value.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
}} // namespace bfg::versioned
|
||||||
|
|
||||||
|
namespace std {
|
||||||
|
|
||||||
|
template <class N, class P, class B>
|
||||||
|
struct hash<::bfg::versioned::semver<N, P, B>>
|
||||||
|
{
|
||||||
|
::std::size_t operator()(
|
||||||
|
const ::bfg::versioned::semver<N, P, B> & value) const noexcept
|
||||||
|
{
|
||||||
|
return ::bfg::versioned::hash(value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#if defined(__clang__)
|
||||||
|
# pragma clang diagnostic push
|
||||||
|
# pragma clang diagnostic ignored "-Wmismatched-tags"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template <class N, class P, class B>
|
||||||
|
struct tuple_size<::bfg::versioned::semver<N, P, B>>
|
||||||
|
: integral_constant<size_t, 3>
|
||||||
|
{ };
|
||||||
|
|
||||||
|
template <size_t I, class N, class P, class B>
|
||||||
|
struct tuple_element<I, ::bfg::versioned::semver<N, P, B>>
|
||||||
|
{
|
||||||
|
using version_t = typename ::bfg::versioned::semver<N, P, B>::version_t;
|
||||||
|
using prerelease_t =
|
||||||
|
typename ::bfg::versioned::semver<N, P, B>::prerelease_t;
|
||||||
|
using build_t = typename ::bfg::versioned::semver<N, P, B>::build_t;
|
||||||
|
using type = const typename conditional<I == 0,
|
||||||
|
version_t,
|
||||||
|
typename conditional<I == 1, prerelease_t, build_t>::type>::type;
|
||||||
|
};
|
||||||
|
|
||||||
|
#if defined(__clang__)
|
||||||
|
# pragma clang diagnostic pop
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} // namespace std
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -6,6 +6,9 @@ Distributed under the Boost Software License, Version 1.0.
|
|||||||
|
|
||||||
#include "mod_version.h"
|
#include "mod_version.h"
|
||||||
|
|
||||||
|
#include "ext_bfgroup_versioned.hpp"
|
||||||
|
#include "value.h"
|
||||||
|
|
||||||
namespace b2 {
|
namespace b2 {
|
||||||
|
|
||||||
bool version_less(const std::vector<int> & lhs, const std::vector<int> & rhs)
|
bool version_less(const std::vector<int> & lhs, const std::vector<int> & rhs)
|
||||||
@@ -26,4 +29,31 @@ bool version_less(const std::vector<int> & lhs, const std::vector<int> & rhs)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool semver_verify(value_ref v)
|
||||||
|
{
|
||||||
|
bfg::versioned::semver<> v_semver;
|
||||||
|
auto v_string = v->as_string();
|
||||||
|
auto v_first = v_string.cbegin();
|
||||||
|
auto v_last = v_first + v_string.size();
|
||||||
|
auto r = from_chars(v_first, v_last, v_semver);
|
||||||
|
return static_cast<bool>(r) && (r.ptr == v_last);
|
||||||
|
}
|
||||||
|
|
||||||
|
list_ref semver_resolve(std::tuple<value_ref, value_ref> ab)
|
||||||
|
{
|
||||||
|
list_ref result;
|
||||||
|
bfg::versioned::semver<> a_semver;
|
||||||
|
bfg::versioned::semver<> b_semver;
|
||||||
|
auto a_string = std::get<0>(ab)->as_string();
|
||||||
|
auto b_string = std::get<1>(ab)->as_string();
|
||||||
|
auto a_result = from_chars(
|
||||||
|
a_string.cbegin(), a_string.cbegin() + a_string.size(), a_semver);
|
||||||
|
auto b_result = from_chars(
|
||||||
|
b_string.cbegin(), b_string.cbegin() + b_string.size(), b_semver);
|
||||||
|
if (a_result && b_result && a_semver.major() == b_semver.major()
|
||||||
|
&& (a_semver < b_semver || a_semver == b_semver))
|
||||||
|
result.push_back(std::get<1>(ab));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace b2
|
} // namespace b2
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ Distributed under the Boost Software License, Version 1.0.
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include "bind.h"
|
#include "bind.h"
|
||||||
|
#include "lists.h"
|
||||||
|
#include "value.h"
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@@ -38,6 +40,23 @@ second version, `rhs`.
|
|||||||
end::reference[] */
|
end::reference[] */
|
||||||
bool version_less(const std::vector<int> & lhs, const std::vector<int> & rhs);
|
bool version_less(const std::vector<int> & lhs, const std::vector<int> & rhs);
|
||||||
|
|
||||||
|
/* tag::reference[]
|
||||||
|
|
||||||
|
== `b2::version_verify`
|
||||||
|
|
||||||
|
====
|
||||||
|
[horizontal]
|
||||||
|
Jam:: `rule semver-verify ( semver )`
|
||||||
|
{CPP}:: `bool semver_verify(value_ref semver);`
|
||||||
|
====
|
||||||
|
|
||||||
|
Returns `true` if the given `semver` value is a fully valid semantic version.
|
||||||
|
|
||||||
|
end::reference[] */
|
||||||
|
bool semver_verify(value_ref semver);
|
||||||
|
|
||||||
|
list_ref semver_resolve(std::tuple<value_ref, value_ref> ab);
|
||||||
|
|
||||||
struct version_module : b2::bind::module_<version_module>
|
struct version_module : b2::bind::module_<version_module>
|
||||||
{
|
{
|
||||||
const char * module_name = "version";
|
const char * module_name = "version";
|
||||||
@@ -45,8 +64,9 @@ struct version_module : b2::bind::module_<version_module>
|
|||||||
template <class Binder>
|
template <class Binder>
|
||||||
void def(Binder & binder)
|
void def(Binder & binder)
|
||||||
{
|
{
|
||||||
binder.def(
|
binder.def(&b2::version_less, "version-less", "lhs" * _1n | "rhs" * _1n);
|
||||||
&b2::version_less, "version-less", "lhs" * _1n | "rhs" * _1n);
|
binder.def(&b2::semver_verify, "semver-verify", "semver" * _1);
|
||||||
|
binder.def(&b2::semver_resolve, "semver-resolve", "a" * _1 + "b" * _1);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} // namespace b2
|
} // namespace b2
|
||||||
|
|||||||
@@ -16,4 +16,4 @@ feature.
|
|||||||
|
|
||||||
feature.feature version
|
feature.feature version
|
||||||
:
|
:
|
||||||
: free ;
|
: version ;
|
||||||
|
|||||||
Reference in New Issue
Block a user