lib.licenses: add compound licenses

This commit is contained in:
jopejoe1
2025-11-09 22:17:25 +01:00
parent 601cd88557
commit bb8e1c54e9
7 changed files with 277 additions and 3 deletions

View File

@@ -268,7 +268,7 @@ pkgs/development/python-modules/buildcatrust/ @ajs124 @lukegb @mweinelt
/pkgs/applications/editors/jetbrains @leona-ya @theCapypara
# Licenses
/lib/licenses.nix @alyssais @emilazy @jopejoe1
/lib/licenses @alyssais @emilazy @jopejoe1
# Qt
/pkgs/development/libraries/qt-5 @K900 @NickCao @SuperSandro2000 @ttuegel

View File

@@ -73,7 +73,7 @@ let
types = callLibs ./types.nix;
# constants
licenses = callLibs ./licenses.nix;
licenses = callLibs ./licenses;
sourceTypes = callLibs ./source-types.nix;
systems = callLibs ./systems;

7
lib/licenses/default.nix Normal file
View File

@@ -0,0 +1,7 @@
{ lib }:
let
licenses = import ./licenses.nix { inherit lib; };
operators = import ./operators.nix;
helpers = import ./helpers.nix { inherit lib; };
in
licenses // operators // helpers

152
lib/licenses/helpers.nix Normal file
View File

@@ -0,0 +1,152 @@
{ lib }:
rec {
/**
Evaluate a license expression for a given predicate.
# Example
```nix
evaluateProperty (x: x.free) true (with lib.licenses; AND [ ncsa (WITH asl20 llvm-exception) ])
```
# Type
```
evaluateProperty :: Function -> Bool -> AttrSet -> Bool
```
# Arguments
- [predicate] checks for each license included in the license expression
- [permissive] whether to apply checks permissive or reciprocal
- [license] license expression to check
*/
evaluateProperty =
predicate: permissive: license:
let
OR = if permissive then lib.any else lib.all;
AND = if permissive then lib.all else lib.any;
in
if license.licenseType == "simple" then
predicate license
else if license.licenseType == "compound" then
if license.operator == "OR" then
OR (x: evaluateProperty predicate permissive x) license.licenses
else if license.operator == "AND" then
AND (x: evaluateProperty predicate permissive x) license.licenses
else
throw "Unknown license operator"
else if license.licenseType == "exception" then
AND (x: evaluateProperty predicate permissive x) [
license.license
license.exception
]
else if license.licenseType == "plus" then
evaluateProperty predicate permissive license.license
else
throw "Unknown license type or legacy license";
/**
Check whether a license expression is free.
# Example
```nix
isFree (with lib.licenses; (AND [ ncsa (WITH asl20 llvm-exception) ]))
=> true
```
# Type
```
isFree :: AttrSet -> Bool
```
# Arguments
- [license] License expression to check if free
*/
isFree = evaluateProperty (x: x.free) true;
/**
Check whether a license expression is redistributable.
# Example
```nix
isRedistributable (with lib.licenses; (AND [ ncsa (WITH asl20 llvm-exception) ]))
=> true
```
# Type
```
isRedistributable :: AttrSet -> Bool
```
# Arguments
- [license] License expression to check if redistributable
*/
isRedistributable = evaluateProperty (x: x.redistributable) true;
/**
Check whether any of the given licenses is required in the license expression.
# Example
```nix
containsLicenses [ lib.licenses.asl20 ] (with lib.licenses; (AND [ ncsa (WITH asl20 llvm-exception) ]))
=> true
```
# Type
```
containsLicenses :: List -> AttrSet -> Bool
```
# Arguments
- [licenses] List of licenses to look
- [license] License expression to check
*/
containsLicenses = licenses: evaluateProperty (x: lib.lists.elem x licenses) false;
/**
Convert a license expression to an SPDX license expression string.
# Example
```nix
toSPDX (with lib.licenses; AND [ ncsa (WITH asl20 llvm-exception) ])
=> "NCSA AND (Apache-2.0 WITH LLVM-exception)"
```
# Type
```
toSPDX :: AttrSet -> String
```
# Arguments
- [license] License expression which to convert to spdx expression
*/
toSPDX =
license:
let
mkBracket =
x:
if x.licenseType == "compound" || x.licenseType == "exception" then "(${toSPDX x})" else toSPDX x;
in
if license.licenseType == "simple" then
license.spdxId or "LicenseRef-nixos-${license.shortName}"
else if license.licenseType == "compound" then
lib.concatMapStringsSep " ${license.operator} " (x: mkBracket x) license.licenses
else if license.licenseType == "exception" then
"${mkBracket license.license} ${license.operator} ${mkBracket license.exception}"
else if license.licenseType == "plus" then
"${mkBracket license.license}${license.operator}"
else
throw "Unknown license type";
}

View File

@@ -21,6 +21,7 @@ let
deprecated
redistributable
;
licenseType = "simple";
}
// optionalAttrs (attrs ? spdxId) {
inherit spdxId;

110
lib/licenses/operators.nix Normal file
View File

@@ -0,0 +1,110 @@
{
/**
This should be used when there is a choice of which license expression to use.
This is a disjunctive binary "OR" operator.
# Example
```nix
OR [ lib.licenses.mit lib.licenses.asl20 ]
=> { licenseType = "compound"; operator = "OR"; licenses = [ lib.licenses.mit lib.licenses.asl20 ] };
```
# Type
```
OR :: List -> AttrSet
```
# Arguments
- [licenses] Possible licenses to choose from
*/
OR = licenses: {
licenseType = "compound";
operator = "OR";
inherit licenses;
};
/**
Create a compound licenses where the user needs to follow both licenses,
eqivialent of spdx `and` modifier.
# Example
```nix
AND [ lib.licenses.mit lib.licenses.asl20 ]
=> { licenseType = "compound"; operator = "AND"; licenses = [ lib.licenses.mit lib.licenses.asl20 ] };
```
# Type
```
AND :: List -> AttrSet
```
# Arguments
- [licenses] Licenses required to use
*/
AND = licenses: {
licenseType = "compound";
operator = "AND";
inherit licenses;
};
/**
Create a licenses exception where a license has a license exception,
eqivialent of spdx `with` modifier.
# Example
```nix
WITH lib.licenses.lgpl21Only lib.licenses.ocamlLgplLinkingException
=> { licenseType = "exception"; operator = "WITH"; license = lib.licenses.lgpl21Only; exception = lib.licenses.ocamlLgplLinkingException; };
```
# Type
```
WITH :: AttrSet -> AttrSet -> AttrSet
```
# Arguments
- [license] License to which the exception applies
- [exception] Exception to apply
*/
WITH = license: exception: {
licenseType = "compound";
operator = "WITH";
inherit license exception;
};
/**
Create a licenses which can be upgraded to any later version of itself,
eqivialent of spdx `+` modifier
# Example
```nix
PLUS lib.licenses.lgpl21Only
=> { licenseType = "plus"; operator = "+"; license = lib.licenses.lgpl21Only; };
```
# Type
```
PLUS :: AttrSet -> AttrSet
```
# Arguments
- [license] License to wich apply an exception
*/
PLUS = license: {
licenseType = "plus";
operator = "+";
inherit license;
};
}

View File

@@ -90,6 +90,8 @@ let
&& (
if isList attrs.meta.license then
any (l: elem l list) attrs.meta.license
else if attrs.meta.license ? "type" then
lib.licenses.containsLicenses list attrs.meta.license
else
elem attrs.meta.license list
);
@@ -103,7 +105,9 @@ let
isUnfree =
licenses:
if isAttrs licenses then
if isAttrs licenses && licenses ? "type" then
!(lib.licenses.isFree licenses)
else if isAttrs licenses then
!(licenses.free or true)
# TODO: Returning false in the case of a string is a bug that should be fixed.
# In a previous implementation of this function the function body