diff --git a/ci/OWNERS b/ci/OWNERS index b8fc4ecf920c..1b4b3e68ef9b 100644 --- a/ci/OWNERS +++ b/ci/OWNERS @@ -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 diff --git a/lib/default.nix b/lib/default.nix index 15949d1cdf36..751738a98965 100644 --- a/lib/default.nix +++ b/lib/default.nix @@ -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; diff --git a/lib/licenses/default.nix b/lib/licenses/default.nix new file mode 100644 index 000000000000..7e260e98833f --- /dev/null +++ b/lib/licenses/default.nix @@ -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 diff --git a/lib/licenses/helpers.nix b/lib/licenses/helpers.nix new file mode 100644 index 000000000000..b234515284f8 --- /dev/null +++ b/lib/licenses/helpers.nix @@ -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"; +} diff --git a/lib/licenses.nix b/lib/licenses/licenses.nix similarity index 99% rename from lib/licenses.nix rename to lib/licenses/licenses.nix index 416891232e84..56f29b690495 100644 --- a/lib/licenses.nix +++ b/lib/licenses/licenses.nix @@ -21,6 +21,7 @@ let deprecated redistributable ; + licenseType = "simple"; } // optionalAttrs (attrs ? spdxId) { inherit spdxId; diff --git a/lib/licenses/operators.nix b/lib/licenses/operators.nix new file mode 100644 index 000000000000..c7dab4a1469a --- /dev/null +++ b/lib/licenses/operators.nix @@ -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; + }; +} diff --git a/pkgs/stdenv/generic/check-meta.nix b/pkgs/stdenv/generic/check-meta.nix index fbf72005f92d..6c3a49c4957c 100644 --- a/pkgs/stdenv/generic/check-meta.nix +++ b/pkgs/stdenv/generic/check-meta.nix @@ -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