1
0
Fork 0

updated spdx related files

pull/4208/head
Rob Bast 2015-07-02 11:23:15 +02:00
parent 79f9158fb2
commit e4118385a0
6 changed files with 259 additions and 119 deletions

5
bin/update-spdx-licenses Normal file → Executable file
View File

@ -5,5 +5,6 @@ require __DIR__ . '/../src/bootstrap.php';
use Composer\Util\SpdxLicensesUpdater; use Composer\Util\SpdxLicensesUpdater;
$licenses = new SpdxLicensesUpdater; $updater = new SpdxLicensesUpdater;
$licenses->update(); $updater->dumpLicenses(__DIR__ . '/../res/spdx-licenses.json');
$updater->dumpExceptions(__DIR__ . '/../res/spdx-exceptions.json');

29
res/spdx-exceptions.json Normal file
View File

@ -0,0 +1,29 @@
{
"Autoconf-exception-2.0": [
"Autoconf exception 2.0"
],
"Autoconf-exception-3.0": [
"Autoconf exception 3.0"
],
"Bison-exception-2.2": [
"Bison exception 2.2"
],
"Classpath-exception-2.0": [
"Classpath exception 2.0"
],
"eCos-exception-2.0": [
"eCos exception 2.0"
],
"Font-exception-2.0": [
"Font exception 2.0"
],
"GCC-exception-2.0": [
"GCC Runtime Library exception 2.0"
],
"GCC-exception-3.1": [
"GCC Runtime Library exception 3.1"
],
"WxWindows-exception-3.1": [
"WxWindows Library Exception 3.1"
]
}

View File

@ -431,10 +431,6 @@
"Eclipse Public License 1.0", "Eclipse Public License 1.0",
true true
], ],
"eCos-2.0": [
"eCos license version 2.0",
false
],
"ECL-1.0": [ "ECL-1.0": [
"Educational Community License v1.0", "Educational Community License v1.0",
true true
@ -499,6 +495,10 @@
"Frameworx Open License 1.0", "Frameworx Open License 1.0",
true true
], ],
"FreeImage": [
"FreeImage Public License v1.0",
false
],
"FTL": [ "FTL": [
"Freetype Project License", "Freetype Project License",
false false
@ -543,78 +543,26 @@
"GNU General Public License v1.0 only", "GNU General Public License v1.0 only",
false false
], ],
"GPL-1.0+": [
"GNU General Public License v1.0 or later",
false
],
"GPL-2.0": [ "GPL-2.0": [
"GNU General Public License v2.0 only", "GNU General Public License v2.0 only",
true true
], ],
"GPL-2.0+": [
"GNU General Public License v2.0 or later",
true
],
"GPL-2.0-with-autoconf-exception": [
"GNU General Public License v2.0 w/Autoconf exception",
true
],
"GPL-2.0-with-bison-exception": [
"GNU General Public License v2.0 w/Bison exception",
true
],
"GPL-2.0-with-classpath-exception": [
"GNU General Public License v2.0 w/Classpath exception",
true
],
"GPL-2.0-with-font-exception": [
"GNU General Public License v2.0 w/Font exception",
true
],
"GPL-2.0-with-GCC-exception": [
"GNU General Public License v2.0 w/GCC Runtime Library exception",
true
],
"GPL-3.0": [ "GPL-3.0": [
"GNU General Public License v3.0 only", "GNU General Public License v3.0 only",
true true
], ],
"GPL-3.0+": [
"GNU General Public License v3.0 or later",
true
],
"GPL-3.0-with-autoconf-exception": [
"GNU General Public License v3.0 w/Autoconf exception",
true
],
"GPL-3.0-with-GCC-exception": [
"GNU General Public License v3.0 w/GCC Runtime Library exception",
true
],
"LGPL-2.1": [ "LGPL-2.1": [
"GNU Lesser General Public License v2.1 only", "GNU Lesser General Public License v2.1 only",
true true
], ],
"LGPL-2.1+": [
"GNU Lesser General Public License v2.1 or later",
true
],
"LGPL-3.0": [ "LGPL-3.0": [
"GNU Lesser General Public License v3.0 only", "GNU Lesser General Public License v3.0 only",
true true
], ],
"LGPL-3.0+": [
"GNU Lesser General Public License v3.0 or later",
true
],
"LGPL-2.0": [ "LGPL-2.0": [
"GNU Library General Public License v2 only", "GNU Library General Public License v2 only",
true true
], ],
"LGPL-2.0+": [
"GNU Library General Public License v2 or later",
true
],
"gnuplot": [ "gnuplot": [
"gnuplot License", "gnuplot License",
false false
@ -927,6 +875,10 @@
"Open LDAP Public License v2.7", "Open LDAP Public License v2.7",
false false
], ],
"OLDAP-2.8": [
"Open LDAP Public License v2.8",
false
],
"OML": [ "OML": [
"Open Market License", "Open Market License",
false false
@ -955,10 +907,6 @@
"Open Software License 3.0", "Open Software License 3.0",
true true
], ],
"OLDAP-2.8": [
"OpenLDAP Public License v2.8",
false
],
"OpenSSL": [ "OpenSSL": [
"OpenSSL License", "OpenSSL License",
false false
@ -1079,10 +1027,6 @@
"Standard ML of New Jersey License", "Standard ML of New Jersey License",
false false
], ],
"StandardML-NJ": [
"Standard ML of New Jersey License",
false
],
"SugarCRM-1.1.3": [ "SugarCRM-1.1.3": [
"SugarCRM Public License v1.1.3", "SugarCRM Public License v1.1.3",
false false
@ -1144,17 +1088,17 @@
true true
], ],
"W3C": [ "W3C": [
"W3C Software Notice and License", "W3C Software Notice and License (2002-12-31)",
true true
], ],
"W3C-19980720": [
"W3C Software Notice and License (1998-07-20)",
false
],
"Wsuipa": [ "Wsuipa": [
"Wsuipa License", "Wsuipa License",
false false
], ],
"WXwindows": [
"wxWindows Library License",
true
],
"Xnet": [ "Xnet": [
"X.Net License", "X.Net License",
true true
@ -1203,6 +1147,10 @@
"Zimbra Public License v1.3", "Zimbra Public License v1.3",
false false
], ],
"Zimbra-1.4": [
"Zimbra Public License v1.4",
false
],
"Zlib": [ "Zlib": [
"zlib License", "zlib License",
true true
@ -1222,5 +1170,9 @@
"ZPL-2.1": [ "ZPL-2.1": [
"Zope Public License 2.1", "Zope Public License 2.1",
false false
],
"ICU": [
"ICU License",
false
] ]
} }

View File

@ -12,8 +12,6 @@
namespace Composer\Util; namespace Composer\Util;
use Composer\Json\JsonFile;
/** /**
* Supports composer array and SPDX tag notation for disjunctive/conjunctive * Supports composer array and SPDX tag notation for disjunctive/conjunctive
* licenses. * licenses.
@ -22,26 +20,16 @@ use Composer\Json\JsonFile;
*/ */
class SpdxLicense class SpdxLicense
{ {
/** /** @var array */
* @var array
*/
private $licenses; private $licenses;
/** @var array */
private $exceptions;
public function __construct() public function __construct()
{ {
$this->loadLicenses(); $this->loadLicenses();
} $this->loadExceptions();
private function loadLicenses()
{
if (is_array($this->licenses)) {
return $this->licenses;
}
$jsonFile = new JsonFile(__DIR__ . '/../../../res/spdx-licenses.json');
$this->licenses = $jsonFile->read();
return $this->licenses;
} }
/** /**
@ -58,18 +46,35 @@ class SpdxLicense
} }
$license = $this->licenses[$identifier]; $license = $this->licenses[$identifier];
$license[] = 'http://spdx.org/licenses/' . $identifier . '.html#licenseText';
// add URL for the license text (it's not included in the json)
$license[2] = 'http://spdx.org/licenses/' . $identifier . '#licenseText';
return $license; return $license;
} }
/** /**
* Returns the short identifier of a license by full name. * Returns license exception metadata by license exception identifier.
* *
* @param string $identifier * @param string $identifier
* *
* @return array|null
*/
public function getExceptionByIdentifier($identifier)
{
if (!isset($this->exceptions[$identifier])) {
return;
}
$license = $this->exceptions[$identifier];
$license[] = 'http://spdx.org/licenses/' . $identifier . '.html#licenseExceptionText';
return $license;
}
/**
* Returns the short identifier of a license (exception) by full name.
*
* @param string $name
*
* @return string * @return string
*/ */
public function getIdentifierByName($name) public function getIdentifierByName($name)
@ -79,11 +84,19 @@ class SpdxLicense
return $identifier; return $identifier;
} }
} }
foreach ($this->exceptions as $identifier => $licenseData) {
if ($licenseData[0] === $name) { // key 0 = fullname
return $identifier;
}
}
} }
/** /**
* Returns the OSI Approved status for a license by identifier. * Returns the OSI Approved status for a license by identifier.
* *
* @param string $identifier
*
* @return bool * @return bool
*/ */
public function isOsiApprovedByIdentifier($identifier) public function isOsiApprovedByIdentifier($identifier)
@ -105,6 +118,20 @@ class SpdxLicense
return in_array($identifier, $identifiers); return in_array($identifier, $identifiers);
} }
/**
* Check, if the identifier for a exception is valid.
*
* @param string $identifier
*
* @return bool
*/
private function isValidExceptionIdentifier($identifier)
{
$identifiers = array_keys($this->exceptions);
return in_array($identifier, $identifiers);
}
/** /**
* @param array|string $license * @param array|string $license
* *
@ -118,18 +145,49 @@ class SpdxLicense
if ($count !== count(array_filter($license, 'is_string'))) { if ($count !== count(array_filter($license, 'is_string'))) {
throw new \InvalidArgumentException('Array of strings expected.'); throw new \InvalidArgumentException('Array of strings expected.');
} }
$license = $count > 1 ? '('.implode(' or ', $license).')' : (string) reset($license); $license = $count > 1 ? '('.implode(' OR ', $license).')' : (string) reset($license);
} }
if (!is_string($license)) { if (!is_string($license)) {
throw new \InvalidArgumentException(sprintf( throw new \InvalidArgumentException(sprintf(
'Array or String expected, %s given.', gettype($license) 'Array or String expected, %s given.',
gettype($license)
)); ));
} }
return $this->isValidLicenseString($license); return $this->isValidLicenseString($license);
} }
/**
* @return array
*/
private function loadLicenses()
{
if (is_array($this->licenses)) {
return $this->licenses;
}
$jsonFile = file_get_contents(__DIR__ . '/../../../res/spdx-licenses.json');
$this->licenses = json_decode($jsonFile, true);
return $this->licenses;
}
/**
* @return array
*/
private function loadExceptions()
{
if (is_array($this->exceptions)) {
return $this->exceptions;
}
$jsonFile = file_get_contents(__DIR__ . '/../../../res/spdx-exceptions.json');
$this->exceptions = json_decode($jsonFile, true);
return $this->exceptions;
}
/** /**
* @param string $license * @param string $license
* *
@ -141,10 +199,11 @@ class SpdxLicense
$tokens = array( $tokens = array(
'po' => '\(', 'po' => '\(',
'pc' => '\)', 'pc' => '\)',
'op' => '(?:or|and)', 'op' => '(?:or|OR|and|AND)',
'wi' => '(?:with|WITH)',
'lix' => '(?:NONE|NOASSERTION)', 'lix' => '(?:NONE|NOASSERTION)',
'lir' => 'LicenseRef-\d+', 'lir' => 'LicenseRef-\d+',
'lic' => '[-+_.a-zA-Z0-9]{3,}', 'lic' => '[-_.a-zA-Z0-9]{3,}\+?',
'ws' => '\s+', 'ws' => '\s+',
'_' => '.', '_' => '.',
); );
@ -171,44 +230,58 @@ class SpdxLicense
return array($name, $matches[0][0]); return array($name, $matches[0][0]);
} }
throw new \RuntimeException('At least the last pattern needs to match, but it did not (dot-match-all is missing?).'); throw new \RuntimeException(
'At least the last pattern needs to match, but it did not (dot-match-all is missing?).'
);
}; };
$open = 0; $open = 0;
$require = 1; $with = false;
$require = true;
$lastop = null; $lastop = null;
while (list($token, $string) = $next()) { while (list($token, $string) = $next()) {
switch ($token) { switch ($token) {
case 'po': case 'po':
if ($open || !$require) { if ($open || !$require || $with) {
return false; return false;
} }
$open = 1; $open = 1;
break; break;
case 'pc': case 'pc':
if ($open !== 1 || $require || !$lastop) { if ($open !== 1 || $require || !$lastop || $with) {
return false; return false;
} }
$open = 2; $open = 2;
break; break;
case 'op': case 'op':
if ($require || !$open) { if ($require || !$open || $with) {
return false; return false;
} }
$lastop || $lastop = $string; $lastop || $lastop = $string;
if ($lastop !== $string) { if ($lastop !== $string) {
return false; return false;
} }
$require = 1; $require = true;
break;
case 'wi':
$with = true;
break; break;
case 'lix': case 'lix':
if ($open) { if ($open || $with) {
return false; return false;
} }
goto lir; goto lir;
case 'lic': case 'lic':
if (!$this->isValidLicenseIdentifier($string)) { if ($with && $this->isValidExceptionIdentifier($string)) {
$require = true;
$with = false;
goto lir;
}
if ($with) {
return false;
}
if (!$this->isValidLicenseIdentifier(rtrim($string, '+'))) {
return false; return false;
} }
// Fall-through intended // Fall-through intended
@ -217,7 +290,7 @@ class SpdxLicense
if (!$require) { if (!$require) {
return false; return false;
} }
$require = 0; $require = false;
break; break;
case 'ws': case 'ws':
break; break;
@ -228,6 +301,6 @@ class SpdxLicense
} }
} }
return !($open % 2 || $require); return !($open % 2 || $require || $with);
} }
} }

View File

@ -12,8 +12,6 @@
namespace Composer\Util; namespace Composer\Util;
use Composer\Json\JsonFormatter;
/** /**
* The SPDX Licenses Updater scrapes licenses from the spdx website * The SPDX Licenses Updater scrapes licenses from the spdx website
* and updates the "res/spdx-licenses.json" file accordingly. * and updates the "res/spdx-licenses.json" file accordingly.
@ -22,21 +20,57 @@ use Composer\Json\JsonFormatter;
*/ */
class SpdxLicensesUpdater class SpdxLicensesUpdater
{ {
private $licensesUrl = 'http://www.spdx.org/licenses/'; /**
* @param string $file
public function update() * @param string $url
*/
public function dumpLicenses($file, $url = 'http://www.spdx.org/licenses/')
{ {
$json = json_encode($this->getLicenses(), true); $options = 0;
$prettyJson = JsonFormatter::format($json, true, true);
file_put_contents(__DIR__ . '/../../../res/spdx-licenses.json', $prettyJson); if (defined('JSON_PRETTY_PRINT')) {
$options |= JSON_PRETTY_PRINT;
} }
private function getLicenses() if (defined('JSON_UNESCAPED_SLASHES')) {
$options |= JSON_UNESCAPED_SLASHES;
}
$licenses = json_encode($this->getLicenses($url), $options);
file_put_contents($file, $licenses);
}
/**
* @param string $file
* @param string $url
*/
public function dumpExceptions($file, $url = 'http://www.spdx.org/licenses/exceptions-index.html')
{
$options = 0;
if (defined('JSON_PRETTY_PRINT')) {
$options |= JSON_PRETTY_PRINT;
}
if (defined('JSON_UNESCAPED_SLASHES')) {
$options |= JSON_UNESCAPED_SLASHES;
}
$exceptions = json_encode($this->getExceptions($url), $options);
file_put_contents($file, $exceptions);
}
/**
* @param string $url
*
* @return array
*/
private function getLicenses($url)
{ {
$licenses = array(); $licenses = array();
$dom = new \DOMDocument; $dom = new \DOMDocument;
$dom->loadHTMLFile($this->licensesUrl); @$dom->loadHTMLFile($url);
$xPath = new \DOMXPath($dom); $xPath = new \DOMXPath($dom);
$trs = $xPath->query('//table//tbody//tr'); $trs = $xPath->query('//table//tbody//tr');
@ -45,8 +79,8 @@ class SpdxLicensesUpdater
foreach ($trs as $tr) { foreach ($trs as $tr) {
$tds = $tr->getElementsByTagName('td'); // get the columns in this row $tds = $tr->getElementsByTagName('td'); // get the columns in this row
if ($tds->length < 4) { if ($tds->length !== 4) {
throw new \Exception('Obtaining the license table failed. Wrong table format. Found less than 4 cells in a row.'); continue;
} }
if (trim($tds->item(3)->nodeValue) == 'License Text') { if (trim($tds->item(3)->nodeValue) == 'License Text') {
@ -56,7 +90,7 @@ class SpdxLicensesUpdater
// The license URL is not scraped intentionally to keep json file size low. // The license URL is not scraped intentionally to keep json file size low.
// It's build when requested, see SpdxLicense->getLicenseByIdentifier(). // It's build when requested, see SpdxLicense->getLicenseByIdentifier().
//$licenseURL = = $tds->item(3)->getAttribute('href'); //$licenseURL = $tds->item(3)->getAttribute('href');
$licenses += array($identifier => array($fullname, $osiApproved)); $licenses += array($identifier => array($fullname, $osiApproved));
} }
@ -64,4 +98,42 @@ class SpdxLicensesUpdater
return $licenses; return $licenses;
} }
/**
* @param string $url
*
* @return array
*/
private function getExceptions($url)
{
$exceptions = array();
$dom = new \DOMDocument;
@$dom->loadHTMLFile($url);
$xPath = new \DOMXPath($dom);
$trs = $xPath->query('//table//tbody//tr');
// iterate over each row in the table
foreach ($trs as $tr) {
$tds = $tr->getElementsByTagName('td'); // get the columns in this row
if ($tds->length !== 3) {
continue;
}
if (trim($tds->item(2)->nodeValue) == 'License Exception Text') {
$fullname = trim($tds->item(0)->nodeValue);
$identifier = trim($tds->item(1)->nodeValue);
// The license URL is not scraped intentionally to keep json file size low.
// It's build when requested, see SpdxLicense->getLicenseExceptionByIdentifier().
//$licenseURL = $tds->item(2)->getAttribute('href');
$exceptions += array($identifier => array($fullname));
}
}
return $exceptions;
}
} }

View File

@ -27,12 +27,18 @@ class SpdxLicenseTest extends TestCase
$valid = array_merge( $valid = array_merge(
array( array(
"MIT", "MIT",
"MIT+",
"NONE", "NONE",
"NOASSERTION", "NOASSERTION",
"LicenseRef-3", "LicenseRef-3",
array("LGPL-2.0", "GPL-3.0+"), array("LGPL-2.0", "GPL-3.0+"),
"(LGPL-2.0 or GPL-3.0+)", "(LGPL-2.0 or GPL-3.0+)",
"(LGPL-2.0 OR GPL-3.0+)",
"(EUDatagrid and GPL-3.0+)", "(EUDatagrid and GPL-3.0+)",
"(EUDatagrid AND GPL-3.0+)",
"GPL-2.0 with Autoconf-exception-2.0",
"GPL-2.0 WITH Autoconf-exception-2.0",
"GPL-2.0+ WITH Autoconf-exception-2.0",
), ),
$identifiers $identifiers
); );
@ -52,7 +58,10 @@ class SpdxLicenseTest extends TestCase
array("The system pwns you"), array("The system pwns you"),
array("()"), array("()"),
array("(MIT)"), array("(MIT)"),
array("(MIT"),
array("MIT)"),
array("MIT NONE"), array("MIT NONE"),
array("MIT AND NONE"),
array("MIT (MIT and MIT)"), array("MIT (MIT and MIT)"),
array("(MIT and MIT) MIT"), array("(MIT and MIT) MIT"),
array(array("LGPL-2.0", "The system pwns you")), array(array("LGPL-2.0", "The system pwns you")),
@ -64,6 +73,10 @@ class SpdxLicenseTest extends TestCase
array("(MIT Or MIT)"), array("(MIT Or MIT)"),
array("(NONE or MIT)"), array("(NONE or MIT)"),
array("(NOASSERTION or MIT)"), array("(NOASSERTION or MIT)"),
array("Autoconf-exception-2.0 WITH MIT"),
array("MIT WITH"),
array("MIT OR"),
array("MIT AND"),
); );
} }