[manifest license] fully implement SPDX, plus general parsing stuff (#334)

* [ParserBase] get ready for parsing SPDX license expressions

* parse SPDX license expressions

* add error message tests

* more tests, plus minor error change

* format

Co-authored-by: nicole mazzuca <mazzucan@outlook.com>
This commit is contained in:
nicole mazzuca 2022-02-01 14:43:06 -08:00 коммит произвёл GitHub
Родитель c76e0def14
Коммит 457b0309ce
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
18 изменённых файлов: 1418 добавлений и 867 удалений

Просмотреть файл

@ -37,16 +37,15 @@ function Transform-JsonFile {
$fileContent = @(
"// Data downloaded from $Uri",
"// Generated by scripts/Generate-SpdxLicenseList.ps1",
"{")
"// Generated by Generate-SpdxLicenseList.ps1")
$json.$OuterName |
Sort-Object -Property $Id -Culture '' |
ForEach-Object {
$fileContent += " `"$($_.$Id)`","
$fileContent += "`"$($_.$Id)`","
}
$fileContent += "}"
$fileContent -join "`n" | Out-File -FilePath $OutFile -Encoding 'utf8'
($fileContent -join "`n") + "`n" `
| Out-File -FilePath $OutFile -Encoding 'utf8' -NoNewline
}
$baseUrl = "https://raw.githubusercontent.com/$GithubRepository/$Commit/json"

Просмотреть файл

@ -55,6 +55,7 @@ namespace vcpkg::msg
LocalizedString() = default;
operator StringView() const { return m_data; }
const std::string& data() const { return m_data; }
std::string extract_data() { return std::exchange(m_data, ""); }
static LocalizedString from_string_unchecked(std::string&& s)
{
@ -87,6 +88,8 @@ namespace vcpkg::msg
}
};
inline const char* to_printf_arg(const msg::LocalizedString& s) { return s.data().c_str(); }
struct LocalizedStringMapLess
{
using is_transparent = void;
@ -110,6 +113,16 @@ namespace vcpkg::msg
inline void print(Color c, const LocalizedString& s) { write_unlocalized_text_to_stdout(c, s); }
inline void print(const LocalizedString& s) { write_unlocalized_text_to_stdout(Color::none, s); }
inline void println(Color c, const LocalizedString& s)
{
write_unlocalized_text_to_stdout(c, s);
write_unlocalized_text_to_stdout(Color::none, "\n");
}
inline void println(const LocalizedString& s)
{
write_unlocalized_text_to_stdout(Color::none, s);
write_unlocalized_text_to_stdout(Color::none, "\n");
}
template<class Message, class... Ts>
void print(Message m, Ts... args)
@ -157,6 +170,8 @@ namespace vcpkg::msg
DECLARE_MSG_ARG(version);
DECLARE_MSG_ARG(list);
DECLARE_MSG_ARG(output);
DECLARE_MSG_ARG(row);
DECLARE_MSG_ARG(column);
#undef DECLARE_MSG_ARG
// These are `...` instead of

Просмотреть файл

@ -400,6 +400,7 @@ namespace vcpkg
return !rhs.m_base.has_value();
}
friend bool operator!=(const Optional& lhs, const Optional& rhs) noexcept { return !(lhs == rhs); }
private:
details::OptionalStorage<T> m_base;

Просмотреть файл

@ -1,6 +1,7 @@
#pragma once
#include <vcpkg/base/cstringview.h>
#include <vcpkg/base/messages.h>
#include <vcpkg/base/optional.h>
#include <vcpkg/base/stringview.h>
#include <vcpkg/base/unicode.h>
@ -42,16 +43,36 @@ namespace vcpkg::Parse
virtual const std::string& get_message() const override;
};
struct SourceLoc
{
Unicode::Utf8Decoder it;
Unicode::Utf8Decoder start_of_line;
int row;
int column;
};
enum class MessageKind
{
Warning,
Error,
};
struct ParseMessage
{
SourceLoc location = {};
msg::LocalizedString message;
msg::LocalizedString format(StringView origin, MessageKind kind) const;
};
struct ParseMessages
{
std::unique_ptr<ParseError> error;
std::vector<ParseMessage> warnings;
};
struct ParserBase
{
struct SourceLoc
{
Unicode::Utf8Decoder it;
Unicode::Utf8Decoder start_of_line;
int row;
int column;
};
ParserBase(StringView text, StringView origin, TextRowCol init_rowcol = {});
static constexpr bool is_whitespace(char32_t ch) { return ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n'; }
@ -110,9 +131,17 @@ namespace vcpkg::Parse
void add_error(std::string message) { add_error(std::move(message), cur_loc()); }
void add_error(std::string message, const SourceLoc& loc);
void add_error(msg::LocalizedString&& message) { add_error(message.extract_data(), cur_loc()); }
void add_error(msg::LocalizedString&& message, const SourceLoc& loc) { add_error(message.extract_data(), loc); }
const Parse::IParseError* get_error() const { return m_err.get(); }
std::unique_ptr<Parse::IParseError> extract_error() { return std::move(m_err); }
void add_warning(msg::LocalizedString&& message) { add_warning(std::move(message), cur_loc()); }
void add_warning(msg::LocalizedString&& message, const SourceLoc& loc);
const IParseError* get_error() const { return m_messages.error.get(); }
std::unique_ptr<IParseError> extract_error() { return std::move(m_messages.error); }
const ParseMessages& messages() const { return m_messages; }
ParseMessages extract_messages() { return std::move(m_messages); }
private:
Unicode::Utf8Decoder m_it;
@ -123,6 +152,6 @@ namespace vcpkg::Parse
StringView m_text;
StringView m_origin;
std::unique_ptr<IParseError> m_err;
ParseMessages m_messages;
};
}

Просмотреть файл

@ -14,12 +14,37 @@ namespace vcpkg::Unicode
StartFour = 4,
};
constexpr static char32_t end_of_file = 0xFFFF'FFFF;
enum class utf8_errc
{
NoError = 0,
InvalidCodeUnit = 1,
InvalidCodePoint = 2,
PairedSurrogates = 3,
UnexpectedContinue = 4,
UnexpectedStart = 5,
UnexpectedEof = 6,
};
const std::error_category& utf8_category() noexcept;
Utf8CodeUnitKind utf8_code_unit_kind(unsigned char code_unit) noexcept;
int utf8_code_unit_count(Utf8CodeUnitKind kind) noexcept;
int utf8_code_unit_count(char code_unit) noexcept;
int utf8_encode_code_point(char (&array)[4], char32_t code_point) noexcept;
// returns {after-current-code-point, error},
// and if error = NoError, then out = parsed code point.
// else, out = end_of_file.
std::pair<const char*, utf8_errc> utf8_decode_code_point(const char* first,
const char* last,
char32_t& out) noexcept;
// uses the C++20 definition
bool is_double_width_code_point(char32_t ch) noexcept;
inline std::string& utf8_append_code_point(std::string& str, char32_t code_point)
{
if (static_cast<uint32_t>(code_point) < 0x80)
@ -52,21 +77,6 @@ namespace vcpkg::Unicode
char32_t utf16_surrogates_to_code_point(char32_t leading, char32_t trailing);
constexpr static char32_t end_of_file = 0xFFFF'FFFF;
enum class utf8_errc
{
NoError = 0,
InvalidCodeUnit = 1,
InvalidCodePoint = 2,
PairedSurrogates = 3,
UnexpectedContinue = 4,
UnexpectedStart = 5,
UnexpectedEof = 6,
};
const std::error_category& utf8_category() noexcept;
inline std::error_code make_error_code(utf8_errc err) noexcept
{
return std::error_code(static_cast<int>(err), utf8_category());
@ -89,6 +99,7 @@ namespace vcpkg::Unicode
struct Utf8Decoder
{
Utf8Decoder() noexcept;
explicit Utf8Decoder(StringView sv) : Utf8Decoder(sv.begin(), sv.end()) { }
Utf8Decoder(const char* first, const char* last) noexcept;
struct sentinel

Просмотреть файл

@ -147,7 +147,17 @@ namespace vcpkg::Util
{
using std::begin;
using std::end;
return std::find_if(begin(cont), end(cont), pred);
// allow cont.begin() to not have the same type as cont.end()
auto it = begin(cont);
auto last = end(cont);
for (; it != last; ++it)
{
if (pred(*it))
{
break;
}
}
return it;
}
template<class Container, class Pred>

Просмотреть файл

@ -4,4 +4,5 @@ namespace vcpkg
{
struct Configuration;
struct RegistryConfig;
struct ManifestConfiguration;
}

Просмотреть файл

@ -77,6 +77,6 @@ namespace vcpkg::PlatformExpression
};
// platform expression parses a platform expression; the EBNF of such is defined in
// /docs/maintainers/manifest-files.md#supports
// https://github.com/microsoft/vcpkg/blob/master/docs/maintainers/manifest-files.md#supports
ExpectedS<Expr> parse_platform_expression(StringView expression, MultipleBinaryOperators multiple_binary_operators);
}

Просмотреть файл

@ -2,6 +2,7 @@
#include <vcpkg/base/fwd/json.h>
#include <vcpkg/fwd/configuration.h>
#include <vcpkg/fwd/vcpkgcmdarguments.h>
#include <vcpkg/base/expected.h>
@ -71,7 +72,12 @@ namespace vcpkg
std::vector<Dependency> dependencies;
std::vector<DependencyOverride> overrides;
std::vector<std::string> default_features;
std::string license; // SPDX license expression
// there are two distinct "empty" states here
// "user did not provide a license" -> nullopt
// "user provided license = null" -> {""}
Optional<std::string> license; // SPDX license expression
Optional<std::string> builtin_baseline;
Optional<Json::Object> vcpkg_configuration;
// Currently contacts is only a Json::Object but it will eventually be unified with maintainers
@ -127,8 +133,7 @@ namespace vcpkg
Json::Object serialize_manifest(const SourceControlFile& scf);
Json::Object serialize_debug_manifest(const SourceControlFile& scf);
ExpectedS<struct ManifestConfiguration> parse_manifest_configuration(StringView origin,
const Json::Object& manifest);
ExpectedS<ManifestConfiguration> parse_manifest_configuration(StringView origin, const Json::Object& manifest);
/// <summary>
/// Named pair of a SourceControlFile and the location of this file
@ -146,4 +151,6 @@ namespace vcpkg
{
return print_error_message({&error_info_list, 1});
}
std::string parse_spdx_license_expression(StringView sv, Parse::ParseMessages& messages);
}

Просмотреть файл

@ -5,6 +5,7 @@
"AwsFailedToDownload": "aws failed to download with exit code: {value}\n{output}",
"AwsRestoredPackages": "Restored {value} packages from AWS servers in {elapsed}s",
"AwsUploadedPackages": "Uploaded binaries to {value} AWS servers",
"EmptyLicenseExpression": "SPDX license expression was empty.",
"ErrorIndividualPackagesUnsupported": "Error: In manifest mode, `vcpkg install` does not support individual package arguments.\nTo install additional packages, edit vcpkg.json and then run `vcpkg install` without any package arguments.",
"ErrorInvalidClassicModeOption": "Error: The option {value} is not supported in classic mode and no manifest was found.",
"ErrorInvalidManifestModeOption": "Error: The option {value} is not supported in manifest mode.",
@ -21,8 +22,30 @@
"ErrorRequirePackagesToInstall": "Error: No packages were listed for installation and no manifest was found.",
"ErrorVcvarsUnsupported": "Error: in triplet {triplet}: Use of Visual Studio's Developer Prompt is unsupported on non-Windows hosts.\nDefine 'VCPKG_CMAKE_SYSTEM_NAME' or 'VCPKG_CHAINLOAD_TOOLCHAIN_FILE' in the triplet file.",
"ForceSystemBinariesOnWeirdPlatforms": "Environment variable VCPKG_FORCE_SYSTEM_BINARIES must be set on arm, s390x, and ppc64le platforms.",
"FormattedParseError": "error: {value}",
"FormattedParseMessageExpression": " on expression: {value}",
"FormattedParseMessageLocation": "{path}:{row}:{column}: ",
"_FormattedParseMessageLocation.comment": "{LOCKED}",
"FormattedParseWarning": "warning: {value}",
"IllegalFeatures": "Error: List of features is not allowed in this contect",
"IllegalPlatformSpec": "Error: Platform qualifier is not allowed in this context",
"LicenseExpressionContainsExtraPlus": "SPDX license expression contains an extra '+'. These are only allowed directly after a license identifier.",
"LicenseExpressionContainsInvalidCharacter": "SPDX license expression contains an invalid character (0x{value:02x} '{value}').",
"LicenseExpressionContainsUnicode": "SPDX license expression contains a unicode character (U+{value:04x} '{pretty_value}'), but these expressions are ASCII-only.",
"LicenseExpressionDocumentRefUnsupported": "The current implementation does not support DocumentRef- SPDX references.",
"LicenseExpressionExpectCompoundFoundParen": "Expected a compound or the end of the string, found a parenthesis.",
"LicenseExpressionExpectCompoundFoundWith": "Expected either AND or OR, found WITH (WITH is only allowed after license names, not parenthesized expressions).",
"LicenseExpressionExpectCompoundFoundWord": "Expected either AND or OR, found a license or exception name: '{value}'.",
"LicenseExpressionExpectCompoundOrWithFoundWord": "Expected either AND, OR, or WITH, found a license or exception name: '{value}'.",
"LicenseExpressionExpectExceptionFoundCompound": "Expected an exception name, found the compound {value}.",
"LicenseExpressionExpectExceptionFoundEof": "Expected an exception name, found the end of the string.",
"LicenseExpressionExpectExceptionFoundParen": "Expected an exception name, found a parenthesis.",
"LicenseExpressionExpectLicenseFoundCompound": "Expected a license name, found the compound {value}.",
"LicenseExpressionExpectLicenseFoundEof": "Expected a license name, found the end of the string.",
"LicenseExpressionExpectLicenseFoundParen": "Expected a license name, found a parenthesis.",
"LicenseExpressionImbalancedParens": "There was a close parenthesis without an opening parenthesis.",
"LicenseExpressionUnknownException": "Unknown license exception identifier '{value}'. Known values are listed at https://spdx.org/licenses/exceptions-index.html",
"LicenseExpressionUnknownLicense": "Unknown license identifier '{value}'. Known values are listed at https://spdx.org/licenses/",
"NoLocalizationForMessages": "No localization for the following messages:",
"ProcessorArchitectureMalformed": "Failed to parse %PROCESSOR_ARCHITECTURE% ({value}) as a valid CPU architecture.",
"ProcessorArchitectureMissing": "The required environment variable %PROCESSOR_ARCHITECTURE% is missing.",

Просмотреть файл

@ -236,8 +236,8 @@ TEST_CASE ("JSON track newlines", "[json]")
REQUIRE(!res);
REQUIRE(res.error()->format() ==
R"(filename:2:1: error: Unexpected character; expected property name
on expression: ,
^
on expression: ,
^
)");
}
@ -247,7 +247,39 @@ TEST_CASE ("JSON duplicated object keys", "[json]")
REQUIRE(!res);
REQUIRE(res.error()->format() ==
R"(filename:1:13: error: Duplicated key "name" in an object
on expression: {"name": 1, "name": 2}
^
on expression: {"name": 1, "name": 2}
^
)");
}
TEST_CASE ("JSON support unicode characters in errors", "[json]")
{
// unicode characters w/ bytes >1
auto res = Json::parse(R"json("Δx/Δt" "")json", "filename");
REQUIRE(!res);
CHECK(res.error()->format() ==
R"(filename:1:9: error: Unexpected character; expected EOF
on expression: "Δx/Δt" ""
^
)");
// full width unicode characters
// note that the A is full width
res = Json::parse(R"json("a" "")json", "filename");
REQUIRE(!res);
CHECK(res.error()->format() ==
R"(filename:1:8: error: Unexpected character; expected EOF
on expression: "姐姐a" ""
^
)");
// incorrect errors in the face of combining characters
// (this test should be fixed once the underlying bug is fixed)
res = Json::parse(R"json("" "")json", "filename");
REQUIRE(!res);
CHECK(res.error()->format() ==
R"(filename:1:6: error: Unexpected character; expected EOF
on expression: "" ""
^
)");
}

Просмотреть файл

@ -1,6 +1,7 @@
#include <catch2/catch.hpp>
#include <vcpkg/base/json.h>
#include <vcpkg/base/system.print.h>
#include <vcpkg/base/util.h>
#include <vcpkg/paragraphs.h>
@ -28,21 +29,40 @@ static Json::Object parse_json_object(StringView sv)
}
else
{
vcpkg::print2("Error found while parsing JSON document:\n", sv, '\n');
Checks::exit_with_message(VCPKG_LINE_INFO, json.error()->format());
}
}
static Parse::ParseExpected<SourceControlFile> test_parse_manifest(StringView sv, bool expect_fail = false)
enum class PrintErrors : bool
{
auto object = parse_json_object(sv);
auto res = SourceControlFile::parse_manifest_object("<test manifest>", object);
if (!res.has_value() && !expect_fail)
No,
Yes,
};
static Parse::ParseExpected<SourceControlFile> test_parse_manifest(const Json::Object& obj,
PrintErrors print = PrintErrors::Yes)
{
auto res = SourceControlFile::parse_manifest_object("<test manifest>", obj);
if (!res.has_value() && print == PrintErrors::Yes)
{
print_error_message(res.error());
}
REQUIRE(res.has_value() == !expect_fail);
return res;
}
static Parse::ParseExpected<SourceControlFile> test_parse_manifest(StringView obj, PrintErrors print = PrintErrors::Yes)
{
return test_parse_manifest(parse_json_object(obj), print);
}
static bool manifest_is_parseable(const Json::Object& obj)
{
return test_parse_manifest(obj, PrintErrors::No).has_value();
}
static bool manifest_is_parseable(StringView obj)
{
return test_parse_manifest(parse_json_object(obj), PrintErrors::No).has_value();
}
static const FeatureFlagSettings feature_flags_with_versioning{false, false, false, true};
static const FeatureFlagSettings feature_flags_without_versioning{false, false, false, false};
@ -113,57 +133,49 @@ TEST_CASE ("manifest versioning", "[manifests]")
CHECK(pgh.core_paragraph->port_version == 0);
}
test_parse_manifest(R"json({
REQUIRE_FALSE(manifest_is_parseable(R"json({
"name": "zlib",
"version-string": "abcd",
"version-semver": "1.2.3-rc3"
})json",
true);
})json"));
test_parse_manifest(R"json({
REQUIRE_FALSE(manifest_is_parseable(R"json({
"name": "zlib",
"version-string": "abcd#1"
})json",
true);
test_parse_manifest(R"json({
})json"));
REQUIRE_FALSE(manifest_is_parseable(R"json({
"name": "zlib",
"version": "abcd#1"
})json",
true);
test_parse_manifest(R"json({
})json"));
REQUIRE_FALSE(manifest_is_parseable(R"json({
"name": "zlib",
"version-date": "abcd#1"
})json",
true);
test_parse_manifest(R"json({
})json"));
REQUIRE_FALSE(manifest_is_parseable(R"json({
"name": "zlib",
"version-semver": "abcd#1"
})json",
true);
})json"));
SECTION ("version syntax")
{
test_parse_manifest(R"json({
REQUIRE_FALSE(manifest_is_parseable(R"json({
"name": "zlib",
"version-semver": "2020-01-01"
})json",
true);
test_parse_manifest(R"json({
})json"));
REQUIRE_FALSE(manifest_is_parseable(R"json({
"name": "zlib",
"version-date": "1.1.1"
})json",
true);
test_parse_manifest(R"json({
})json"));
REQUIRE(manifest_is_parseable(R"json({
"name": "zlib",
"version": "1.2.3-rc3"
})json",
false);
})json"));
}
}
TEST_CASE ("manifest constraints hash", "[manifests]")
{
auto p = unwrap(test_parse_manifest(R"json({
auto m_pgh = test_parse_manifest(R"json({
"name": "zlib",
"version-string": "abcd",
"dependencies": [
@ -172,11 +184,13 @@ TEST_CASE ("manifest constraints hash", "[manifests]")
"version>=": "2018-09-01#1"
}
]
})json"));
})json");
REQUIRE(m_pgh.has_value());
const auto& p = *m_pgh.get();
REQUIRE(p->core_paragraph->dependencies.at(0).constraint.value == "2018-09-01");
REQUIRE(p->core_paragraph->dependencies.at(0).constraint.port_version == 1);
test_parse_manifest(R"json({
REQUIRE_FALSE(manifest_is_parseable(R"json({
"name": "zlib",
"version-string": "abcd",
"dependencies": [
@ -185,10 +199,9 @@ TEST_CASE ("manifest constraints hash", "[manifests]")
"version>=": "2018-09-01#0"
}
]
})json",
true);
})json"));
test_parse_manifest(R"json({
REQUIRE_FALSE(manifest_is_parseable(R"json({
"name": "zlib",
"version-string": "abcd",
"dependencies": [
@ -197,10 +210,9 @@ TEST_CASE ("manifest constraints hash", "[manifests]")
"version>=": "2018-09-01#-1"
}
]
})json",
true);
})json"));
test_parse_manifest(R"json({
REQUIRE_FALSE(manifest_is_parseable(R"json({
"name": "zlib",
"version-string": "abcd",
"dependencies": [
@ -210,13 +222,12 @@ TEST_CASE ("manifest constraints hash", "[manifests]")
"port-version": 1
}
]
})json",
true);
})json"));
}
TEST_CASE ("manifest overrides embedded port version", "[manifests]")
{
test_parse_manifest(R"json({
REQUIRE_FALSE(manifest_is_parseable(R"json({
"name": "zlib",
"version-string": "abcd",
"overrides": [
@ -226,9 +237,8 @@ TEST_CASE ("manifest overrides embedded port version", "[manifests]")
"port-version": 1
}
]
})json",
true);
test_parse_manifest(R"json({
})json"));
REQUIRE_FALSE(manifest_is_parseable(R"json({
"name": "zlib",
"version-string": "abcd",
"overrides": [
@ -238,9 +248,8 @@ TEST_CASE ("manifest overrides embedded port version", "[manifests]")
"port-version": 1
}
]
})json",
true);
test_parse_manifest(R"json({
})json"));
REQUIRE_FALSE(manifest_is_parseable(R"json({
"name": "zlib",
"version-string": "abcd",
"overrides": [
@ -250,9 +259,8 @@ TEST_CASE ("manifest overrides embedded port version", "[manifests]")
"port-version": 1
}
]
})json",
true);
test_parse_manifest(R"json({
})json"));
REQUIRE_FALSE(manifest_is_parseable(R"json({
"name": "zlib",
"version-string": "abcd",
"overrides": [
@ -262,10 +270,9 @@ TEST_CASE ("manifest overrides embedded port version", "[manifests]")
"port-version": 1
}
]
})json",
true);
})json"));
CHECK(unwrap(test_parse_manifest(R"json({
auto parsed = test_parse_manifest(R"json({
"name": "zlib",
"version-string": "abcd",
"overrides": [
@ -274,11 +281,11 @@ TEST_CASE ("manifest overrides embedded port version", "[manifests]")
"version-string": "abcd#1"
}
]
})json",
false))
->core_paragraph->overrides.at(0)
.port_version == 1);
CHECK(unwrap(test_parse_manifest(R"json({
})json");
REQUIRE(parsed.has_value());
CHECK((*parsed.get())->core_paragraph->overrides.at(0).port_version == 1);
parsed = test_parse_manifest(R"json({
"name": "zlib",
"version-string": "abcd",
"overrides": [
@ -287,11 +294,11 @@ TEST_CASE ("manifest overrides embedded port version", "[manifests]")
"version-date": "2018-01-01#1"
}
]
})json",
false))
->core_paragraph->overrides.at(0)
.port_version == 1);
CHECK(unwrap(test_parse_manifest(R"json({
})json");
REQUIRE(parsed.has_value());
CHECK((*parsed.get())->core_paragraph->overrides.at(0).port_version == 1);
parsed = test_parse_manifest(R"json({
"name": "zlib",
"version-string": "abcd",
"overrides": [
@ -300,11 +307,11 @@ TEST_CASE ("manifest overrides embedded port version", "[manifests]")
"version": "1.2#1"
}
]
})json",
false))
->core_paragraph->overrides.at(0)
.port_version == 1);
CHECK(unwrap(test_parse_manifest(R"json({
})json");
REQUIRE(parsed.has_value());
CHECK((*parsed.get())->core_paragraph->overrides.at(0).port_version == 1);
parsed = test_parse_manifest(R"json({
"name": "zlib",
"version-string": "abcd",
"overrides": [
@ -313,10 +320,9 @@ TEST_CASE ("manifest overrides embedded port version", "[manifests]")
"version-semver": "1.2.0#1"
}
]
})json",
false))
->core_paragraph->overrides.at(0)
.port_version == 1);
})json");
REQUIRE(parsed.has_value());
CHECK((*parsed.get())->core_paragraph->overrides.at(0).port_version == 1);
}
TEST_CASE ("manifest constraints", "[manifests]")
@ -355,7 +361,7 @@ TEST_CASE ("manifest constraints", "[manifests]")
DependencyConstraint{VersionConstraintKind::Minimum, "2018-09-01", 0});
REQUIRE(pgh.core_paragraph->builtin_baseline == "089fa4de7dca22c67dcab631f618d5cd0697c8d4");
test_parse_manifest(R"json({
REQUIRE_FALSE(manifest_is_parseable(R"json({
"name": "zlib",
"version-string": "abcd",
"dependencies": [
@ -364,8 +370,7 @@ TEST_CASE ("manifest constraints", "[manifests]")
"port-version": 5
}
]
})json",
true);
})json"));
}
TEST_CASE ("manifest builtin-baseline", "[manifests]")
@ -555,7 +560,7 @@ TEST_CASE ("manifest overrides", "[manifests]")
REQUIRE(!pgh.check_against_feature_flags({}, feature_flags_with_versioning));
}
test_parse_manifest(R"json({
REQUIRE_FALSE(manifest_is_parseable(R"json({
"name": "zlib",
"version-string": "abcd",
"builtin-baseline": "089fa4de7dca22c67dcab631f618d5cd0697c8d4",
@ -565,10 +570,9 @@ TEST_CASE ("manifest overrides", "[manifests]")
"version-semver": "1.2.3-rc3",
"version-string": "1.2.3-rc3"
}
]})json",
true);
]})json"));
test_parse_manifest(R"json({
REQUIRE_FALSE(manifest_is_parseable(R"json({
"name": "zlib",
"version-string": "abcd",
"builtin-baseline": "089fa4de7dca22c67dcab631f618d5cd0697c8d4",
@ -577,8 +581,7 @@ TEST_CASE ("manifest overrides", "[manifests]")
"name": "abc",
"port-version": 5
}
]})json",
true);
]})json"));
std::string raw = R"json({
"name": "zlib",
@ -946,22 +949,146 @@ TEST_CASE ("SourceParagraph manifest supports", "[manifests]")
TEST_CASE ("SourceParagraph manifest empty supports", "[manifests]")
{
auto m_pgh = test_parse_manifest(R"json({
REQUIRE_FALSE(manifest_is_parseable(R"json({
"name": "a",
"version-string": "1.0",
"supports": ""
})json",
true);
REQUIRE_FALSE(m_pgh.has_value());
})json"));
}
TEST_CASE ("SourceParagraph manifest non-string supports", "[manifests]")
{
auto m_pgh = test_parse_manifest(R"json({
REQUIRE_FALSE(manifest_is_parseable(R"json({
"name": "a",
"version-string": "1.0",
"supports": true
})json",
true);
REQUIRE_FALSE(m_pgh.has_value());
})json"));
}
static Json::Object manifest_with_license(Json::Value&& license)
{
Json::Object res;
res.insert("name", Json::Value::string("foo"));
res.insert("version", Json::Value::string("0"));
res.insert("license", std::move(license));
return res;
}
static Json::Object manifest_with_license(StringView license)
{
return manifest_with_license(Json::Value::string(license.to_string()));
}
static std::string test_serialized_license(StringView license)
{
auto m_pgh = test_parse_manifest(manifest_with_license(license));
REQUIRE(m_pgh.has_value());
return serialize_manifest(**m_pgh.get())["license"].string().to_string();
}
static bool license_is_parseable(StringView license)
{
Parse::ParseMessages messages;
parse_spdx_license_expression(license, messages);
return messages.error == nullptr;
}
static bool license_is_strict(StringView license)
{
Parse::ParseMessages messages;
parse_spdx_license_expression(license, messages);
return messages.error == nullptr && messages.warnings.empty();
}
static std::string test_format_parse_warning(const Parse::ParseMessage& msg)
{
return msg.format("<license string>", Parse::MessageKind::Warning).extract_data();
}
TEST_CASE ("simple license in manifest", "[manifests][license]")
{
CHECK(manifest_is_parseable(manifest_with_license(Json::Value::null(nullptr))));
CHECK_FALSE(manifest_is_parseable(manifest_with_license("")));
CHECK(manifest_is_parseable(manifest_with_license("MIT")));
}
TEST_CASE ("valid and invalid licenses", "[manifests][license]")
{
CHECK(license_is_strict("mIt"));
CHECK(license_is_strict("Apache-2.0"));
CHECK(license_is_strict("GPL-2.0+"));
CHECK_FALSE(license_is_parseable("GPL-2.0++"));
CHECK(license_is_strict("LicenseRef-blah"));
CHECK_FALSE(license_is_strict("unknownlicense"));
CHECK(license_is_parseable("unknownlicense"));
}
TEST_CASE ("licenses with compounds", "[manifests][license]")
{
CHECK(license_is_strict("GPL-3.0+ WITH GCC-exception-3.1"));
CHECK(license_is_strict("Apache-2.0 WITH LLVM-exception"));
CHECK_FALSE(license_is_parseable("(Apache-2.0) WITH LLVM-exception"));
CHECK(license_is_strict("(Apache-2.0 OR MIT) AND GPL-3.0+ WITH GCC-exception-3.1"));
CHECK_FALSE(license_is_parseable("Apache-2.0 WITH"));
CHECK_FALSE(license_is_parseable("GPL-3.0+ AND"));
CHECK_FALSE(license_is_parseable("MIT and Apache-2.0"));
CHECK_FALSE(license_is_parseable("GPL-3.0 WITH GCC-exception+"));
CHECK_FALSE(license_is_parseable("(GPL-3.0 WITH GCC-exception)+"));
}
TEST_CASE ("license serialization", "[manifests][license]")
{
auto m_pgh = test_parse_manifest(manifest_with_license(Json::Value::null(nullptr)));
REQUIRE(m_pgh);
auto manifest = serialize_manifest(**m_pgh.get());
REQUIRE(manifest.contains("license"));
CHECK(manifest["license"].is_null());
CHECK(test_serialized_license("MIT") == "MIT");
CHECK(test_serialized_license("mit") == "MIT");
CHECK(test_serialized_license("MiT AND (aPACHe-2.0 \tOR \n gpl-2.0+)") == "MIT AND (Apache-2.0 OR GPL-2.0+)");
CHECK(test_serialized_license("uNkNoWnLiCeNsE") == "uNkNoWnLiCeNsE");
}
TEST_CASE ("license error messages", "[manifests][license]")
{
Parse::ParseMessages messages;
parse_spdx_license_expression("", messages);
REQUIRE(messages.error);
CHECK(messages.error->format() == R"(<license string>:1:1: error: SPDX license expression was empty.
on expression:
^
)");
parse_spdx_license_expression("MIT ()", messages);
REQUIRE(messages.error);
CHECK(messages.error->format() ==
R"(<license string>:1:5: error: Expected a compound or the end of the string, found a parenthesis.
on expression: MIT ()
^
)");
parse_spdx_license_expression("MIT +", messages);
REQUIRE(messages.error);
CHECK(
messages.error->format() ==
R"(<license string>:1:5: error: SPDX license expression contains an extra '+'. These are only allowed directly after a license identifier.
on expression: MIT +
^
)");
parse_spdx_license_expression("MIT AND", messages);
REQUIRE(messages.error);
CHECK(messages.error->format() ==
R"(<license string>:1:8: error: Expected a license name, found the end of the string.
on expression: MIT AND
^
)");
parse_spdx_license_expression("MIT AND unknownlicense", messages);
CHECK(!messages.error);
REQUIRE(messages.warnings.size() == 1);
CHECK(
test_format_parse_warning(messages.warnings[0]) ==
R"(<license string>:1:9: warning: Unknown license identifier 'unknownlicense'. Known values are listed at https://spdx.org/licenses/
on expression: MIT AND unknownlicense
^)");
}

Просмотреть файл

@ -1,7 +1,9 @@
#include <vcpkg/base/messages.h>
#include <vcpkg/base/parse.h>
#include <vcpkg/base/system.print.h>
#include <vcpkg/base/util.h>
#include <algorithm>
#include <utility>
using namespace vcpkg;
@ -25,28 +27,67 @@ namespace vcpkg::Parse
std::string ParseError::format() const
{
auto caret_spacing = std::string(18, ' ');
auto decoder = Unicode::Utf8Decoder(line.data(), line.data() + line.size());
for (int i = 0; i < caret_col; ++i, ++decoder)
{
const char32_t cp = *decoder;
// this may eventually want to check for full-width characters and grapheme clusters as well
caret_spacing.push_back(cp == '\t' ? '\t' : ' ');
}
ParseMessage as_message;
as_message.location = SourceLoc{std::next(decoder, caret_col), decoder, row, column};
as_message.message = msg::LocalizedString::from_string_unchecked(std::string(message));
return Strings::concat(origin,
":",
row,
":",
column,
": error: ",
message,
"\n"
" on expression: ", // 18 columns
line,
"\n",
caret_spacing,
"^\n");
auto res = as_message.format(origin, MessageKind::Error).extract_data();
res.push_back('\n');
return res;
}
DECLARE_AND_REGISTER_MESSAGE(FormattedParseMessageLocation,
(msg::path, msg::row, msg::column),
"{LOCKED}",
"{path}:{row}:{column}: ");
DECLARE_AND_REGISTER_MESSAGE(FormattedParseError, (msg::value), "", "error: {value}");
DECLARE_AND_REGISTER_MESSAGE(FormattedParseWarning, (msg::value), "", "warning: {value}");
DECLARE_AND_REGISTER_MESSAGE(FormattedParseMessageExpression, (msg::value), "", " on expression: {value}");
msg::LocalizedString ParseMessage::format(StringView origin, MessageKind kind) const
{
msg::LocalizedString res = msg::format(msgFormattedParseMessageLocation,
msg::path = origin,
msg::row = location.row,
msg::column = location.column);
if (kind == MessageKind::Warning)
{
res.append(msg::format(msgFormattedParseWarning, msg::value = message));
}
else
{
res.append(msg::format(msgFormattedParseError, msg::value = message));
}
res.appendnl();
auto line_end = Util::find_if(location.it, Parse::ParserBase::is_lineend);
StringView line = StringView{
location.start_of_line.pointer_to_current(),
line_end.pointer_to_current(),
};
res.append(msg::format(msgFormattedParseMessageExpression, msg::value = line));
res.appendnl();
auto caret_point = StringView{location.start_of_line.pointer_to_current(), location.it.pointer_to_current()};
auto formatted_caret_point = msg::format(msgFormattedParseMessageExpression, msg::value = caret_point);
std::string caret_string;
caret_string.reserve(formatted_caret_point.data().size());
for (char32_t ch : Unicode::Utf8Decoder(formatted_caret_point))
{
if (ch == '\t')
caret_string.push_back('\t');
else if (Unicode::is_double_width_code_point(ch))
caret_string.append(" ");
else
caret_string.push_back(' ');
}
caret_string.push_back('^');
res.append(msg::LocalizedString::from_string_unchecked(std::move(caret_string)));
return res;
}
const std::string& ParseError::get_message() const { return this->message; }
@ -84,10 +125,15 @@ namespace vcpkg::Parse
return cur();
}
void ParserBase::add_warning(msg::LocalizedString&& message, const SourceLoc& loc)
{
m_messages.warnings.push_back(ParseMessage{loc, std::move(message)});
}
void ParserBase::add_error(std::string message, const SourceLoc& loc)
{
// avoid cascading errors by only saving the first
if (!m_err)
if (!m_messages.error)
{
// find end of line
auto line_end = loc.it;
@ -95,7 +141,7 @@ namespace vcpkg::Parse
{
++line_end;
}
m_err = std::make_unique<ParseError>(
m_messages.error = std::make_unique<ParseError>(
m_origin.to_string(),
loc.row,
loc.column,

Просмотреть файл

@ -92,6 +92,103 @@ namespace vcpkg::Unicode
return count;
}
std::pair<const char*, utf8_errc> utf8_decode_code_point(const char* first,
const char* last,
char32_t& out) noexcept
{
out = end_of_file;
if (first == last)
{
return {last, utf8_errc::NoError};
}
auto code_unit = *first;
auto kind = utf8_code_unit_kind(code_unit);
const int count = utf8_code_unit_count(kind);
const char* it = first + 1;
if (kind == Utf8CodeUnitKind::Invalid)
{
return {it, utf8_errc::InvalidCodeUnit};
}
else if (kind == Utf8CodeUnitKind::Continue)
{
return {it, utf8_errc::UnexpectedContinue};
}
else if (count > last - first)
{
return {last, utf8_errc::UnexpectedEof};
}
if (count == 1)
{
out = static_cast<char32_t>(code_unit);
return {it, utf8_errc::NoError};
}
// 2 -> 0b0001'1111, 6
// 3 -> 0b0000'1111, 12
// 4 -> 0b0000'0111, 18
const auto start_mask = static_cast<unsigned char>(0xFF >> (count + 1));
const int start_shift = 6 * (count - 1);
char32_t code_point = static_cast<char32_t>(code_unit & start_mask) << start_shift;
constexpr unsigned char continue_mask = 0b0011'1111;
for (int byte = 1; byte < count; ++byte)
{
code_unit = static_cast<unsigned char>(*it++);
kind = utf8_code_unit_kind(code_unit);
if (kind == Utf8CodeUnitKind::Invalid)
{
return {it, utf8_errc::InvalidCodeUnit};
}
else if (kind != Utf8CodeUnitKind::Continue)
{
return {it, utf8_errc::UnexpectedStart};
}
const int shift = 6 * (count - byte - 1);
code_point |= (code_unit & continue_mask) << shift;
}
if (code_point > 0x10'FFFF)
{
return {it, utf8_errc::InvalidCodePoint};
}
out = code_point;
return {it, utf8_errc::NoError};
}
// uses the C++20 definition
/*
[format.string.std]
* U+1100 - U+115F
* U+2329 - U+232A
* U+2E80 - U+303E
* U+3040 - U+A4CF
* U+AC00 - U+D7A3
* U+F900 - U+FAFF
* U+FE10 - U+FE19
* U+FE30 - U+FE6F
* U+FF00 - U+FF60
* U+FFE0 - U+FFE6
* U+1F300 - U+1F64F
* U+1F900 - U+1F9FF
* U+20000 - U+2FFFD
* U+30000 - U+3FFFD
*/
bool is_double_width_code_point(char32_t ch) noexcept
{
return (ch >= 0x1100 && ch <= 0x115F) || (ch >= 0x2329 && ch <= 0x232A) || (ch >= 0x2E80 && ch <= 0x303E) ||
(ch >= 0x3040 && ch <= 0xA4CF) || (ch >= 0xAC00 && ch <= 0xD7A3) || (ch >= 0xF900 && ch <= 0xFAFF) ||
(ch >= 0xFE10 && ch <= 0xFE19) || (ch >= 0xFE30 && ch <= 0xFE6F) || (ch >= 0xFF00 && ch <= 0xFF60) ||
(ch >= 0xFFE0 && ch <= 0xFFE6) || (ch >= 0x1F300 && ch <= 0x1F64F) || (ch >= 0x1F900 && ch <= 0x1F9FF) ||
(ch >= 0x20000 && ch <= 0x2FFFD) || (ch >= 0x30000 && ch <= 0x3FFFD);
}
bool utf8_is_valid_string(const char* first, const char* last) noexcept
{
utf8_errc err = utf8_errc::NoError;
@ -179,76 +276,22 @@ namespace vcpkg::Unicode
return utf8_errc::NoError;
}
unsigned char code_unit = static_cast<unsigned char>(*next_++);
auto kind = utf8_code_unit_kind(code_unit);
if (kind == Utf8CodeUnitKind::Invalid)
char32_t code_point;
auto new_next = utf8_decode_code_point(next_, last_, code_point);
if (new_next.second != utf8_errc::NoError)
{
*this = sentinel();
return utf8_errc::InvalidCodeUnit;
return new_next.second;
}
else if (kind == Utf8CodeUnitKind::Continue)
if (utf16_is_trailing_surrogate_code_point(code_point) && utf16_is_leading_surrogate_code_point(current_))
{
*this = sentinel();
return utf8_errc::UnexpectedContinue;
return utf8_errc::PairedSurrogates;
}
const int count = utf8_code_unit_count(kind);
if (count == 1)
{
current_ = static_cast<char32_t>(code_unit);
}
else
{
// 2 -> 0b0001'1111, 6
// 3 -> 0b0000'1111, 12
// 4 -> 0b0000'0111, 18
const auto start_mask = static_cast<unsigned char>(0xFF >> (count + 1));
const int start_shift = 6 * (count - 1);
auto code_point = static_cast<char32_t>(code_unit & start_mask) << start_shift;
constexpr unsigned char continue_mask = 0b0011'1111;
for (int byte = 1; byte < count; ++byte)
{
if (next_ == last_)
{
*this = sentinel();
return utf8_errc::UnexpectedContinue;
}
code_unit = static_cast<unsigned char>(*next_++);
kind = utf8_code_unit_kind(code_unit);
if (kind == Utf8CodeUnitKind::Invalid)
{
*this = sentinel();
return utf8_errc::InvalidCodeUnit;
}
else if (kind != Utf8CodeUnitKind::Continue)
{
*this = sentinel();
return utf8_errc::UnexpectedStart;
}
const int shift = 6 * (count - byte - 1);
code_point |= (code_unit & continue_mask) << shift;
}
if (code_point > 0x10'FFFF)
{
*this = sentinel();
return utf8_errc::InvalidCodePoint;
}
else if (utf16_is_trailing_surrogate_code_point(code_point) &&
utf16_is_leading_surrogate_code_point(current_))
{
*this = sentinel();
return utf8_errc::PairedSurrogates;
}
else
{
current_ = code_point;
}
}
next_ = new_next.first;
current_ = code_point;
return utf8_errc::NoError;
}

Просмотреть файл

@ -38,6 +38,8 @@ namespace
"Restored {value} packages from AWS servers in {elapsed}s");
DECLARE_AND_REGISTER_MESSAGE(AwsUploadedPackages, (msg::value), "", "Uploaded binaries to {value} AWS servers");
using Parse::SourceLoc;
struct ConfigSegmentsParser : Parse::ParserBase
{
using Parse::ParserBase::ParserBase;
@ -134,10 +136,9 @@ namespace
}
}
std::vector<std::vector<std::pair<Parse::ParserBase::SourceLoc, std::string>>> ConfigSegmentsParser::
parse_all_segments()
std::vector<std::vector<std::pair<SourceLoc, std::string>>> ConfigSegmentsParser::parse_all_segments()
{
std::vector<std::vector<std::pair<Parse::ParserBase::SourceLoc, std::string>>> ret;
std::vector<std::vector<std::pair<SourceLoc, std::string>>> ret;
while (!at_eof())
{
std::vector<std::pair<SourceLoc, std::string>> segments;

Просмотреть файл

@ -2,6 +2,7 @@
#include <vcpkg/base/expected.h>
#include <vcpkg/base/jsonreader.h>
#include <vcpkg/base/span.h>
#include <vcpkg/base/stringliteral.h>
#include <vcpkg/base/system.debug.h>
#include <vcpkg/base/system.print.h>
#include <vcpkg/base/util.h>
@ -16,6 +17,85 @@
#include <vcpkg/vcpkgcmdarguments.h>
#include <vcpkg/versiondeserializers.h>
namespace
{
namespace msg = vcpkg::msg;
DECLARE_AND_REGISTER_MESSAGE(EmptyLicenseExpression, (), "", "SPDX license expression was empty.");
DECLARE_AND_REGISTER_MESSAGE(LicenseExpressionContainsUnicode,
(msg::value, msg::pretty_value),
"",
"SPDX license expression contains a unicode character (U+{value:04x} "
"'{pretty_value}'), but these expressions are ASCII-only.");
DECLARE_AND_REGISTER_MESSAGE(LicenseExpressionContainsInvalidCharacter,
(msg::value),
"",
"SPDX license expression contains an invalid character (0x{value:02x} '{value}').");
DECLARE_AND_REGISTER_MESSAGE(LicenseExpressionContainsExtraPlus,
(),
"",
"SPDX license expression contains an extra '+'. These are only allowed directly "
"after a license identifier.");
DECLARE_AND_REGISTER_MESSAGE(LicenseExpressionDocumentRefUnsupported,
(),
"",
"The current implementation does not support DocumentRef- SPDX references.");
DECLARE_AND_REGISTER_MESSAGE(LicenseExpressionExpectLicenseFoundEof,
(),
"",
"Expected a license name, found the end of the string.");
DECLARE_AND_REGISTER_MESSAGE(LicenseExpressionExpectExceptionFoundEof,
(),
"",
"Expected an exception name, found the end of the string.");
DECLARE_AND_REGISTER_MESSAGE(LicenseExpressionExpectCompoundFoundParen,
(),
"",
"Expected a compound or the end of the string, found a parenthesis.");
DECLARE_AND_REGISTER_MESSAGE(LicenseExpressionExpectLicenseFoundParen,
(),
"",
"Expected a license name, found a parenthesis.");
DECLARE_AND_REGISTER_MESSAGE(LicenseExpressionExpectExceptionFoundParen,
(),
"",
"Expected an exception name, found a parenthesis.");
DECLARE_AND_REGISTER_MESSAGE(LicenseExpressionImbalancedParens,
(),
"",
"There was a close parenthesis without an opening parenthesis.");
DECLARE_AND_REGISTER_MESSAGE(LicenseExpressionExpectLicenseFoundCompound,
(msg::value),
"",
"Expected a license name, found the compound {value}.");
DECLARE_AND_REGISTER_MESSAGE(LicenseExpressionExpectExceptionFoundCompound,
(msg::value),
"",
"Expected an exception name, found the compound {value}.");
DECLARE_AND_REGISTER_MESSAGE(LicenseExpressionExpectCompoundFoundWith,
(),
"",
"Expected either AND or OR, found WITH (WITH is only allowed after license names, not "
"parenthesized expressions).");
DECLARE_AND_REGISTER_MESSAGE(LicenseExpressionExpectCompoundOrWithFoundWord,
(msg::value),
"",
"Expected either AND, OR, or WITH, found a license or exception name: '{value}'.");
DECLARE_AND_REGISTER_MESSAGE(LicenseExpressionExpectCompoundFoundWord,
(msg::value),
"",
"Expected either AND or OR, found a license or exception name: '{value}'.");
DECLARE_AND_REGISTER_MESSAGE(
LicenseExpressionUnknownLicense,
(msg::value),
"",
"Unknown license identifier '{value}'. Known values are listed at https://spdx.org/licenses/");
DECLARE_AND_REGISTER_MESSAGE(LicenseExpressionUnknownException,
(msg::value),
"",
"Unknown license exception identifier '{value}'. Known values are listed at "
"https://spdx.org/licenses/exceptions-index.html");
} // anonymous namespace
namespace vcpkg
{
using namespace vcpkg::Parse;
@ -708,143 +788,253 @@ namespace vcpkg
};
ContactsDeserializer ContactsDeserializer::instance;
static constexpr StringLiteral EXPRESSION_WORDS[] = {
"WITH",
"AND",
"OR",
static constexpr StringLiteral VALID_LICENSES[] = {
#include "spdx-licenses.inc"
};
static constexpr StringLiteral VALID_EXCEPTIONS[] = {
#include "spdx-exceptions.inc"
};
static constexpr StringLiteral VALID_LICENSES[] =
#include "spdx-licenses.inc"
;
static constexpr StringLiteral VALID_EXCEPTIONS[] =
#include "spdx-licenses.inc"
;
// We "parse" this so that we can add actual license parsing at some point in the future
// without breaking anyone
// The "license" field; either:
// * a string, which must be an SPDX license expression.
// EBNF located at: https://github.com/microsoft/vcpkg/blob/master/docs/maintainers/manifest-files.md#license
// * `null`, for when the license of the package cannot be described by an SPDX expression
struct SpdxLicenseExpressionParser : Parse::ParserBase
{
SpdxLicenseExpressionParser(StringView sv, StringView origin) : Parse::ParserBase(sv, origin) { }
static const StringLiteral* case_insensitive_find(View<StringLiteral> lst, StringView id)
{
return Util::find_if(lst,
[id](StringLiteral el) { return Strings::case_insensitive_ascii_equals(id, el); });
}
static constexpr bool is_idstring_element(char32_t ch) { return is_alphanumdash(ch) || ch == '.'; }
enum class Expecting
{
License, // at the beginning, or after a compound (AND, OR)
Exception, // after a WITH
CompoundOrWith, // after a license
Compound, // after an exception (only one WITH is allowed), or after a close paren
};
void eat_idstring(std::string& result, Expecting& expecting)
{
auto loc = cur_loc();
auto token = match_zero_or_more(is_idstring_element);
if (Strings::starts_with(token, "DocumentRef-"))
{
add_error(msg::format(msgLicenseExpressionDocumentRefUnsupported), loc);
if (cur() == ':')
{
next();
}
return;
}
else if (token == "AND" || token == "OR" || token == "WITH")
{
if (expecting == Expecting::License)
{
add_error(msg::format(msgLicenseExpressionExpectLicenseFoundCompound, msg::value = token), loc);
}
if (expecting == Expecting::Exception)
{
add_error(msg::format(msgLicenseExpressionExpectExceptionFoundCompound, msg::value = token), loc);
}
if (token == "WITH")
{
if (expecting == Expecting::Compound)
{
add_error(msg::format(msgLicenseExpressionExpectCompoundFoundWith), loc);
}
expecting = Expecting::Exception;
}
else
{
expecting = Expecting::License;
}
result.push_back(' ');
result.append(token.begin(), token.end());
result.push_back(' ');
return;
}
switch (expecting)
{
case Expecting::Compound:
add_error(msg::format(msgLicenseExpressionExpectCompoundFoundWord, msg::value = token), loc);
break;
case Expecting::CompoundOrWith:
add_error(msg::format(msgLicenseExpressionExpectCompoundOrWithFoundWord, msg::value = token), loc);
break;
case Expecting::License:
if (Strings::starts_with(token, "LicenseRef-"))
{
result.append(token.begin(), token.end());
}
else
{
auto it = case_insensitive_find(VALID_LICENSES, token);
if (it != std::end(VALID_LICENSES))
{
result.append(it->begin(), it->end());
}
else
{
add_warning(msg::format(msgLicenseExpressionUnknownLicense, msg::value = token), loc);
result.append(token.begin(), token.end());
}
if (cur() == '+')
{
next();
result.push_back('+');
}
}
expecting = Expecting::CompoundOrWith;
break;
case Expecting::Exception:
auto it = case_insensitive_find(VALID_EXCEPTIONS, token);
if (it != std::end(VALID_EXCEPTIONS))
{
// case normalization
result.append(it->begin(), it->end());
}
else
{
add_warning(msg::format(msgLicenseExpressionUnknownException, msg::value = token), loc);
result.append(token.begin(), token.end());
}
expecting = Expecting::Compound;
break;
}
}
std::string parse()
{
if (cur() == Unicode::end_of_file)
{
add_error(msg::format(msgEmptyLicenseExpression));
return "";
}
Expecting expecting = Expecting::License;
std::string result;
size_t open_parens = 0;
while (!at_eof())
{
skip_whitespace();
switch (cur())
{
case '(':
if (expecting == Expecting::Compound || expecting == Expecting::CompoundOrWith)
{
add_error(msg::format(msgLicenseExpressionExpectCompoundFoundParen));
}
if (expecting == Expecting::Exception)
{
add_error(msg::format(msgLicenseExpressionExpectExceptionFoundParen));
}
result.push_back('(');
expecting = Expecting::License;
++open_parens;
next();
break;
case ')':
if (expecting == Expecting::License)
{
add_error(msg::format(msgLicenseExpressionExpectLicenseFoundParen));
}
else if (expecting == Expecting::Exception)
{
add_error(msg::format(msgLicenseExpressionExpectExceptionFoundParen));
}
if (open_parens == 0)
{
add_error(msg::format(msgLicenseExpressionImbalancedParens));
}
result.push_back(')');
expecting = Expecting::Compound;
--open_parens;
next();
break;
case '+':
add_error(msg::format(msgLicenseExpressionContainsExtraPlus));
next();
break;
default:
if (cur() > 0x7F)
{
auto ch = cur();
auto first = it().pointer_to_current();
next();
auto last = it().pointer_to_current();
add_error(msg::format(msgLicenseExpressionContainsUnicode,
msg::value = ch,
msg::pretty_value = StringView{first, last}));
break;
}
if (!is_idstring_element(cur()))
{
add_error(msg::format(msgLicenseExpressionContainsInvalidCharacter,
msg::value = static_cast<char>(cur())));
next();
break;
}
eat_idstring(result, expecting);
break;
}
}
if (expecting == Expecting::License)
{
add_error(msg::format(msgLicenseExpressionExpectLicenseFoundEof));
}
if (expecting == Expecting::Exception)
{
add_error(msg::format(msgLicenseExpressionExpectExceptionFoundEof));
}
return result;
}
};
std::string parse_spdx_license_expression(StringView sv, Parse::ParseMessages& messages)
{
auto parser = SpdxLicenseExpressionParser(sv, "<license string>");
auto result = parser.parse();
messages = parser.extract_messages();
return result;
}
struct LicenseExpressionDeserializer : Json::IDeserializer<std::string>
{
virtual StringView type_name() const override { return "an SPDX license expression"; }
enum class Mode
virtual Optional<std::string> visit_null(Json::Reader&) override { return {std::string()}; }
// if `sv` is a valid SPDX license expression, returns sv,
// but with whitespace normalized
virtual Optional<std::string> visit_string(Json::Reader& r, StringView sv) override
{
ExpectExpression,
ExpectContinue,
ExpectException,
};
auto parser = SpdxLicenseExpressionParser(sv, "<manifest>");
auto res = parser.parse();
virtual Optional<std::string> visit_string(Json::Reader&, StringView sv) override
{
Mode mode = Mode::ExpectExpression;
size_t open_parens = 0;
std::string current_word;
const auto check_current_word = [&current_word, &mode] {
if (current_word.empty())
{
return true;
}
Span<const StringLiteral> valid_ids;
bool case_sensitive = false;
switch (mode)
{
case Mode::ExpectExpression:
valid_ids = VALID_LICENSES;
mode = Mode::ExpectContinue;
// a single + is allowed on the end of licenses
if (current_word.back() == '+')
{
current_word.pop_back();
}
break;
case Mode::ExpectContinue:
valid_ids = EXPRESSION_WORDS;
mode = Mode::ExpectExpression;
case_sensitive = true;
break;
case Mode::ExpectException:
valid_ids = VALID_EXCEPTIONS;
mode = Mode::ExpectContinue;
break;
}
const auto equal = [&](StringView sv) {
if (case_sensitive)
{
return sv == current_word;
}
else
{
return Strings::case_insensitive_ascii_equals(sv, current_word);
}
};
if (std::find_if(valid_ids.begin(), valid_ids.end(), equal) == valid_ids.end())
{
return false;
}
if (current_word == "WITH")
{
mode = Mode::ExpectException;
}
current_word.clear();
return true;
};
for (const auto& ch : sv)
for (const auto& warning : parser.messages().warnings)
{
if (ch == ' ' || ch == '\t')
{
if (!check_current_word())
{
return nullopt;
}
}
else if (ch == '(')
{
if (!check_current_word())
{
return nullopt;
}
if (mode != Mode::ExpectExpression)
{
return nullopt;
}
++open_parens;
}
else if (ch == ')')
{
if (!check_current_word())
{
return nullopt;
}
if (mode != Mode::ExpectContinue)
{
return nullopt;
}
if (open_parens == 0)
{
return nullopt;
}
--open_parens;
}
else
{
current_word.push_back(ch);
}
msg::println(Color::warning, warning.format("<manifest>", Parse::MessageKind::Warning));
}
if (auto err = parser.get_error())
{
r.add_generic_error(type_name(), err->format());
return std::string();
}
if (!check_current_word())
{
return nullopt;
}
else
{
return sv.to_string();
}
return res;
}
static LicenseExpressionDeserializer instance;
@ -941,7 +1131,13 @@ namespace vcpkg
r.optional_object_field(obj, DESCRIPTION, spgh->description, Json::ParagraphDeserializer::instance);
r.optional_object_field(obj, HOMEPAGE, spgh->homepage, url_deserializer);
r.optional_object_field(obj, DOCUMENTATION, spgh->documentation, url_deserializer);
r.optional_object_field(obj, LICENSE, spgh->license, LicenseExpressionDeserializer::instance);
std::string license;
if (r.optional_object_field(obj, LICENSE, license, LicenseExpressionDeserializer::instance))
{
spgh->license = {std::move(license)};
}
r.optional_object_field(obj, DEPENDENCIES, spgh->dependencies, DependencyArrayDeserializer::instance);
static Json::ArrayDeserializer<DependencyOverrideDeserializer> overrides_deserializer{
"an array of overrides"};
@ -1464,7 +1660,21 @@ namespace vcpkg
serialize_optional_string(obj, ManifestDeserializer::HOMEPAGE, scf.core_paragraph->homepage);
serialize_optional_string(obj, ManifestDeserializer::DOCUMENTATION, scf.core_paragraph->documentation);
serialize_optional_string(obj, ManifestDeserializer::LICENSE, scf.core_paragraph->license);
if (auto license = scf.core_paragraph->license.get())
{
if (license->empty())
{
obj.insert(ManifestDeserializer::LICENSE, Json::Value::null(nullptr));
}
else
{
obj.insert(ManifestDeserializer::LICENSE, Json::Value::string(*license));
}
}
else if (debug)
{
obj.insert(ManifestDeserializer::LICENSE, Json::Value::string(""));
}
serialize_optional_string(
obj, ManifestDeserializer::SUPPORTS, to_string(scf.core_paragraph->supports_expression));
if (scf.core_paragraph->builtin_baseline.has_value())

Просмотреть файл

@ -1,45 +1,43 @@
// Data downloaded from https://raw.githubusercontent.com/spdx/license-list-data/0af22f869f8b0906097cfac90ee0516992e8939f/json/exceptions.json
// Generated by scripts/Generate-SpdxLicenseList.ps1
{
"389-exception",
"Autoconf-exception-2.0",
"Autoconf-exception-3.0",
"Bison-exception-2.2",
"Bootloader-exception",
"Classpath-exception-2.0",
"CLISP-exception-2.0",
"DigiRule-FOSS-exception",
"eCos-exception-2.0",
"Fawkes-Runtime-exception",
"FLTK-exception",
"Font-exception-2.0",
"freertos-exception-2.0",
"GCC-exception-2.0",
"GCC-exception-3.1",
"gnu-javamail-exception",
"GPL-3.0-linking-exception",
"GPL-3.0-linking-source-exception",
"GPL-CC-1.0",
"i2p-gpl-java-exception",
"LGPL-3.0-linking-exception",
"Libtool-exception",
"Linux-syscall-note",
"LLVM-exception",
"LZMA-exception",
"mif-exception",
"Nokia-Qt-exception-1.1",
"OCaml-LGPL-linking-exception",
"OCCT-exception-1.0",
"OpenJDK-assembly-exception-1.0",
"openvpn-openssl-exception",
"PS-or-PDF-font-exception-20170817",
"Qt-GPL-exception-1.0",
"Qt-LGPL-exception-1.1",
"Qwt-exception-1.0",
"SHL-2.0",
"SHL-2.1",
"Swift-exception",
"u-boot-exception-2.0",
"Universal-FOSS-exception-1.0",
"WxWindows-exception-3.1",
}
// Generated by Generate-SpdxLicenseList.ps1
"389-exception",
"Autoconf-exception-2.0",
"Autoconf-exception-3.0",
"Bison-exception-2.2",
"Bootloader-exception",
"Classpath-exception-2.0",
"CLISP-exception-2.0",
"DigiRule-FOSS-exception",
"eCos-exception-2.0",
"Fawkes-Runtime-exception",
"FLTK-exception",
"Font-exception-2.0",
"freertos-exception-2.0",
"GCC-exception-2.0",
"GCC-exception-3.1",
"gnu-javamail-exception",
"GPL-3.0-linking-exception",
"GPL-3.0-linking-source-exception",
"GPL-CC-1.0",
"i2p-gpl-java-exception",
"LGPL-3.0-linking-exception",
"Libtool-exception",
"Linux-syscall-note",
"LLVM-exception",
"LZMA-exception",
"mif-exception",
"Nokia-Qt-exception-1.1",
"OCaml-LGPL-linking-exception",
"OCCT-exception-1.0",
"OpenJDK-assembly-exception-1.0",
"openvpn-openssl-exception",
"PS-or-PDF-font-exception-20170817",
"Qt-GPL-exception-1.0",
"Qt-LGPL-exception-1.1",
"Qwt-exception-1.0",
"SHL-2.0",
"SHL-2.1",
"Swift-exception",
"u-boot-exception-2.0",
"Universal-FOSS-exception-1.0",
"WxWindows-exception-3.1",

Просмотреть файл

@ -1,479 +1,477 @@
// Data downloaded from https://raw.githubusercontent.com/spdx/license-list-data/0af22f869f8b0906097cfac90ee0516992e8939f/json/licenses.json
// Generated by scripts/Generate-SpdxLicenseList.ps1
{
"0BSD",
"AAL",
"Abstyles",
"Adobe-2006",
"Adobe-Glyph",
"ADSL",
"AFL-1.1",
"AFL-1.2",
"AFL-2.0",
"AFL-2.1",
"AFL-3.0",
"Afmparse",
"AGPL-1.0",
"AGPL-1.0-only",
"AGPL-1.0-or-later",
"AGPL-3.0",
"AGPL-3.0-only",
"AGPL-3.0-or-later",
"Aladdin",
"AMDPLPA",
"AML",
"AMPAS",
"ANTLR-PD",
"ANTLR-PD-fallback",
"Apache-1.0",
"Apache-1.1",
"Apache-2.0",
"APAFML",
"APL-1.0",
"APSL-1.0",
"APSL-1.1",
"APSL-1.2",
"APSL-2.0",
"Artistic-1.0",
"Artistic-1.0-cl8",
"Artistic-1.0-Perl",
"Artistic-2.0",
"Bahyph",
"Barr",
"Beerware",
"BitTorrent-1.0",
"BitTorrent-1.1",
"blessing",
"BlueOak-1.0.0",
"Borceux",
"BSD-1-Clause",
"BSD-2-Clause",
"BSD-2-Clause-FreeBSD",
"BSD-2-Clause-NetBSD",
"BSD-2-Clause-Patent",
"BSD-2-Clause-Views",
"BSD-3-Clause",
"BSD-3-Clause-Attribution",
"BSD-3-Clause-Clear",
"BSD-3-Clause-LBNL",
"BSD-3-Clause-Modification",
"BSD-3-Clause-No-Military-License",
"BSD-3-Clause-No-Nuclear-License",
"BSD-3-Clause-No-Nuclear-License-2014",
"BSD-3-Clause-No-Nuclear-Warranty",
"BSD-3-Clause-Open-MPI",
"BSD-4-Clause",
"BSD-4-Clause-Shortened",
"BSD-4-Clause-UC",
"BSD-Protection",
"BSD-Source-Code",
"BSL-1.0",
"BUSL-1.1",
"bzip2-1.0.5",
"bzip2-1.0.6",
"C-UDA-1.0",
"CAL-1.0",
"CAL-1.0-Combined-Work-Exception",
"Caldera",
"CATOSL-1.1",
"CC-BY-1.0",
"CC-BY-2.0",
"CC-BY-2.5",
"CC-BY-2.5-AU",
"CC-BY-3.0",
"CC-BY-3.0-AT",
"CC-BY-3.0-DE",
"CC-BY-3.0-NL",
"CC-BY-3.0-US",
"CC-BY-4.0",
"CC-BY-NC-1.0",
"CC-BY-NC-2.0",
"CC-BY-NC-2.5",
"CC-BY-NC-3.0",
"CC-BY-NC-3.0-DE",
"CC-BY-NC-4.0",
"CC-BY-NC-ND-1.0",
"CC-BY-NC-ND-2.0",
"CC-BY-NC-ND-2.5",
"CC-BY-NC-ND-3.0",
"CC-BY-NC-ND-3.0-DE",
"CC-BY-NC-ND-3.0-IGO",
"CC-BY-NC-ND-4.0",
"CC-BY-NC-SA-1.0",
"CC-BY-NC-SA-2.0",
"CC-BY-NC-SA-2.0-FR",
"CC-BY-NC-SA-2.0-UK",
"CC-BY-NC-SA-2.5",
"CC-BY-NC-SA-3.0",
"CC-BY-NC-SA-3.0-DE",
"CC-BY-NC-SA-3.0-IGO",
"CC-BY-NC-SA-4.0",
"CC-BY-ND-1.0",
"CC-BY-ND-2.0",
"CC-BY-ND-2.5",
"CC-BY-ND-3.0",
"CC-BY-ND-3.0-DE",
"CC-BY-ND-4.0",
"CC-BY-SA-1.0",
"CC-BY-SA-2.0",
"CC-BY-SA-2.0-UK",
"CC-BY-SA-2.1-JP",
"CC-BY-SA-2.5",
"CC-BY-SA-3.0",
"CC-BY-SA-3.0-AT",
"CC-BY-SA-3.0-DE",
"CC-BY-SA-4.0",
"CC-PDDC",
"CC0-1.0",
"CDDL-1.0",
"CDDL-1.1",
"CDL-1.0",
"CDLA-Permissive-1.0",
"CDLA-Permissive-2.0",
"CDLA-Sharing-1.0",
"CECILL-1.0",
"CECILL-1.1",
"CECILL-2.0",
"CECILL-2.1",
"CECILL-B",
"CECILL-C",
"CERN-OHL-1.1",
"CERN-OHL-1.2",
"CERN-OHL-P-2.0",
"CERN-OHL-S-2.0",
"CERN-OHL-W-2.0",
"ClArtistic",
"CNRI-Jython",
"CNRI-Python",
"CNRI-Python-GPL-Compatible",
"Condor-1.1",
"copyleft-next-0.3.0",
"copyleft-next-0.3.1",
"CPAL-1.0",
"CPL-1.0",
"CPOL-1.02",
"Crossword",
"CrystalStacker",
"CUA-OPL-1.0",
"Cube",
"curl",
"D-FSL-1.0",
"diffmark",
"DOC",
"Dotseqn",
"DRL-1.0",
"DSDP",
"dvipdfm",
"ECL-1.0",
"ECL-2.0",
"eCos-2.0",
"EFL-1.0",
"EFL-2.0",
"eGenix",
"Entessa",
"EPICS",
"EPL-1.0",
"EPL-2.0",
"ErlPL-1.1",
"etalab-2.0",
"EUDatagrid",
"EUPL-1.0",
"EUPL-1.1",
"EUPL-1.2",
"Eurosym",
"Fair",
"Frameworx-1.0",
"FreeBSD-DOC",
"FreeImage",
"FSFAP",
"FSFUL",
"FSFULLR",
"FTL",
"GD",
"GFDL-1.1",
"GFDL-1.1-invariants-only",
"GFDL-1.1-invariants-or-later",
"GFDL-1.1-no-invariants-only",
"GFDL-1.1-no-invariants-or-later",
"GFDL-1.1-only",
"GFDL-1.1-or-later",
"GFDL-1.2",
"GFDL-1.2-invariants-only",
"GFDL-1.2-invariants-or-later",
"GFDL-1.2-no-invariants-only",
"GFDL-1.2-no-invariants-or-later",
"GFDL-1.2-only",
"GFDL-1.2-or-later",
"GFDL-1.3",
"GFDL-1.3-invariants-only",
"GFDL-1.3-invariants-or-later",
"GFDL-1.3-no-invariants-only",
"GFDL-1.3-no-invariants-or-later",
"GFDL-1.3-only",
"GFDL-1.3-or-later",
"Giftware",
"GL2PS",
"Glide",
"Glulxe",
"GLWTPL",
"gnuplot",
"GPL-1.0",
"GPL-1.0-only",
"GPL-1.0-or-later",
"GPL-1.0+",
"GPL-2.0",
"GPL-2.0-only",
"GPL-2.0-or-later",
"GPL-2.0-with-autoconf-exception",
"GPL-2.0-with-bison-exception",
"GPL-2.0-with-classpath-exception",
"GPL-2.0-with-font-exception",
"GPL-2.0-with-GCC-exception",
"GPL-2.0+",
"GPL-3.0",
"GPL-3.0-only",
"GPL-3.0-or-later",
"GPL-3.0-with-autoconf-exception",
"GPL-3.0-with-GCC-exception",
"GPL-3.0+",
"gSOAP-1.3b",
"HaskellReport",
"Hippocratic-2.1",
"HPND",
"HPND-sell-variant",
"HTMLTIDY",
"IBM-pibs",
"ICU",
"IJG",
"ImageMagick",
"iMatix",
"Imlib2",
"Info-ZIP",
"Intel",
"Intel-ACPI",
"Interbase-1.0",
"IPA",
"IPL-1.0",
"ISC",
"JasPer-2.0",
"JPNIC",
"JSON",
"LAL-1.2",
"LAL-1.3",
"Latex2e",
"Leptonica",
"LGPL-2.0",
"LGPL-2.0-only",
"LGPL-2.0-or-later",
"LGPL-2.0+",
"LGPL-2.1",
"LGPL-2.1-only",
"LGPL-2.1-or-later",
"LGPL-2.1+",
"LGPL-3.0",
"LGPL-3.0-only",
"LGPL-3.0-or-later",
"LGPL-3.0+",
"LGPLLR",
"Libpng",
"libpng-2.0",
"libselinux-1.0",
"libtiff",
"LiLiQ-P-1.1",
"LiLiQ-R-1.1",
"LiLiQ-Rplus-1.1",
"Linux-OpenIB",
"LPL-1.0",
"LPL-1.02",
"LPPL-1.0",
"LPPL-1.1",
"LPPL-1.2",
"LPPL-1.3a",
"LPPL-1.3c",
"MakeIndex",
"MirOS",
"MIT",
"MIT-0",
"MIT-advertising",
"MIT-CMU",
"MIT-enna",
"MIT-feh",
"MIT-Modern-Variant",
"MIT-open-group",
"MITNFA",
"Motosoto",
"mpich2",
"MPL-1.0",
"MPL-1.1",
"MPL-2.0",
"MPL-2.0-no-copyleft-exception",
"MS-PL",
"MS-RL",
"MTLL",
"MulanPSL-1.0",
"MulanPSL-2.0",
"Multics",
"Mup",
"NAIST-2003",
"NASA-1.3",
"Naumen",
"NBPL-1.0",
"NCGL-UK-2.0",
"NCSA",
"Net-SNMP",
"NetCDF",
"Newsletr",
"NGPL",
"NIST-PD",
"NIST-PD-fallback",
"NLOD-1.0",
"NLOD-2.0",
"NLPL",
"Nokia",
"NOSL",
"Noweb",
"NPL-1.0",
"NPL-1.1",
"NPOSL-3.0",
"NRL",
"NTP",
"NTP-0",
"Nunit",
"O-UDA-1.0",
"OCCT-PL",
"OCLC-2.0",
"ODbL-1.0",
"ODC-By-1.0",
"OFL-1.0",
"OFL-1.0-no-RFN",
"OFL-1.0-RFN",
"OFL-1.1",
"OFL-1.1-no-RFN",
"OFL-1.1-RFN",
"OGC-1.0",
"OGDL-Taiwan-1.0",
"OGL-Canada-2.0",
"OGL-UK-1.0",
"OGL-UK-2.0",
"OGL-UK-3.0",
"OGTSL",
"OLDAP-1.1",
"OLDAP-1.2",
"OLDAP-1.3",
"OLDAP-1.4",
"OLDAP-2.0",
"OLDAP-2.0.1",
"OLDAP-2.1",
"OLDAP-2.2",
"OLDAP-2.2.1",
"OLDAP-2.2.2",
"OLDAP-2.3",
"OLDAP-2.4",
"OLDAP-2.5",
"OLDAP-2.6",
"OLDAP-2.7",
"OLDAP-2.8",
"OML",
"OpenSSL",
"OPL-1.0",
"OPUBL-1.0",
"OSET-PL-2.1",
"OSL-1.0",
"OSL-1.1",
"OSL-2.0",
"OSL-2.1",
"OSL-3.0",
"Parity-6.0.0",
"Parity-7.0.0",
"PDDL-1.0",
"PHP-3.0",
"PHP-3.01",
"Plexus",
"PolyForm-Noncommercial-1.0.0",
"PolyForm-Small-Business-1.0.0",
"PostgreSQL",
"PSF-2.0",
"psfrag",
"psutils",
"Python-2.0",
"Qhull",
"QPL-1.0",
"Rdisc",
"RHeCos-1.1",
"RPL-1.1",
"RPL-1.5",
"RPSL-1.0",
"RSA-MD",
"RSCPL",
"Ruby",
"SAX-PD",
"Saxpath",
"SCEA",
"Sendmail",
"Sendmail-8.23",
"SGI-B-1.0",
"SGI-B-1.1",
"SGI-B-2.0",
"SHL-0.5",
"SHL-0.51",
"SimPL-2.0",
"SISSL",
"SISSL-1.2",
"Sleepycat",
"SMLNJ",
"SMPPL",
"SNIA",
"Spencer-86",
"Spencer-94",
"Spencer-99",
"SPL-1.0",
"SSH-OpenSSH",
"SSH-short",
"SSPL-1.0",
"StandardML-NJ",
"SugarCRM-1.1.3",
"SWL",
"TAPR-OHL-1.0",
"TCL",
"TCP-wrappers",
"TMate",
"TORQUE-1.1",
"TOSL",
"TU-Berlin-1.0",
"TU-Berlin-2.0",
"UCL-1.0",
"Unicode-DFS-2015",
"Unicode-DFS-2016",
"Unicode-TOU",
"Unlicense",
"UPL-1.0",
"Verbatim-man-pages",
"Vim",
"VOSTROM",
"VSL-1.0",
"W3C",
"W3C-19980720",
"W3C-20150513",
"Watcom-1.0",
"Wsuipa",
"WTFPL",
"wxWindows",
"X11",
"Xerox",
"XFree86-1.1",
"xinetd",
"Xnet",
"xpp",
"XSkat",
"YPL-1.0",
"YPL-1.1",
"Zed",
"Zend-2.0",
"Zimbra-1.3",
"Zimbra-1.4",
"Zlib",
"zlib-acknowledgement",
"ZPL-1.1",
"ZPL-2.0",
"ZPL-2.1",
}
// Generated by Generate-SpdxLicenseList.ps1
"0BSD",
"AAL",
"Abstyles",
"Adobe-2006",
"Adobe-Glyph",
"ADSL",
"AFL-1.1",
"AFL-1.2",
"AFL-2.0",
"AFL-2.1",
"AFL-3.0",
"Afmparse",
"AGPL-1.0",
"AGPL-1.0-only",
"AGPL-1.0-or-later",
"AGPL-3.0",
"AGPL-3.0-only",
"AGPL-3.0-or-later",
"Aladdin",
"AMDPLPA",
"AML",
"AMPAS",
"ANTLR-PD",
"ANTLR-PD-fallback",
"Apache-1.0",
"Apache-1.1",
"Apache-2.0",
"APAFML",
"APL-1.0",
"APSL-1.0",
"APSL-1.1",
"APSL-1.2",
"APSL-2.0",
"Artistic-1.0",
"Artistic-1.0-cl8",
"Artistic-1.0-Perl",
"Artistic-2.0",
"Bahyph",
"Barr",
"Beerware",
"BitTorrent-1.0",
"BitTorrent-1.1",
"blessing",
"BlueOak-1.0.0",
"Borceux",
"BSD-1-Clause",
"BSD-2-Clause",
"BSD-2-Clause-FreeBSD",
"BSD-2-Clause-NetBSD",
"BSD-2-Clause-Patent",
"BSD-2-Clause-Views",
"BSD-3-Clause",
"BSD-3-Clause-Attribution",
"BSD-3-Clause-Clear",
"BSD-3-Clause-LBNL",
"BSD-3-Clause-Modification",
"BSD-3-Clause-No-Military-License",
"BSD-3-Clause-No-Nuclear-License",
"BSD-3-Clause-No-Nuclear-License-2014",
"BSD-3-Clause-No-Nuclear-Warranty",
"BSD-3-Clause-Open-MPI",
"BSD-4-Clause",
"BSD-4-Clause-Shortened",
"BSD-4-Clause-UC",
"BSD-Protection",
"BSD-Source-Code",
"BSL-1.0",
"BUSL-1.1",
"bzip2-1.0.5",
"bzip2-1.0.6",
"C-UDA-1.0",
"CAL-1.0",
"CAL-1.0-Combined-Work-Exception",
"Caldera",
"CATOSL-1.1",
"CC-BY-1.0",
"CC-BY-2.0",
"CC-BY-2.5",
"CC-BY-2.5-AU",
"CC-BY-3.0",
"CC-BY-3.0-AT",
"CC-BY-3.0-DE",
"CC-BY-3.0-NL",
"CC-BY-3.0-US",
"CC-BY-4.0",
"CC-BY-NC-1.0",
"CC-BY-NC-2.0",
"CC-BY-NC-2.5",
"CC-BY-NC-3.0",
"CC-BY-NC-3.0-DE",
"CC-BY-NC-4.0",
"CC-BY-NC-ND-1.0",
"CC-BY-NC-ND-2.0",
"CC-BY-NC-ND-2.5",
"CC-BY-NC-ND-3.0",
"CC-BY-NC-ND-3.0-DE",
"CC-BY-NC-ND-3.0-IGO",
"CC-BY-NC-ND-4.0",
"CC-BY-NC-SA-1.0",
"CC-BY-NC-SA-2.0",
"CC-BY-NC-SA-2.0-FR",
"CC-BY-NC-SA-2.0-UK",
"CC-BY-NC-SA-2.5",
"CC-BY-NC-SA-3.0",
"CC-BY-NC-SA-3.0-DE",
"CC-BY-NC-SA-3.0-IGO",
"CC-BY-NC-SA-4.0",
"CC-BY-ND-1.0",
"CC-BY-ND-2.0",
"CC-BY-ND-2.5",
"CC-BY-ND-3.0",
"CC-BY-ND-3.0-DE",
"CC-BY-ND-4.0",
"CC-BY-SA-1.0",
"CC-BY-SA-2.0",
"CC-BY-SA-2.0-UK",
"CC-BY-SA-2.1-JP",
"CC-BY-SA-2.5",
"CC-BY-SA-3.0",
"CC-BY-SA-3.0-AT",
"CC-BY-SA-3.0-DE",
"CC-BY-SA-4.0",
"CC-PDDC",
"CC0-1.0",
"CDDL-1.0",
"CDDL-1.1",
"CDL-1.0",
"CDLA-Permissive-1.0",
"CDLA-Permissive-2.0",
"CDLA-Sharing-1.0",
"CECILL-1.0",
"CECILL-1.1",
"CECILL-2.0",
"CECILL-2.1",
"CECILL-B",
"CECILL-C",
"CERN-OHL-1.1",
"CERN-OHL-1.2",
"CERN-OHL-P-2.0",
"CERN-OHL-S-2.0",
"CERN-OHL-W-2.0",
"ClArtistic",
"CNRI-Jython",
"CNRI-Python",
"CNRI-Python-GPL-Compatible",
"Condor-1.1",
"copyleft-next-0.3.0",
"copyleft-next-0.3.1",
"CPAL-1.0",
"CPL-1.0",
"CPOL-1.02",
"Crossword",
"CrystalStacker",
"CUA-OPL-1.0",
"Cube",
"curl",
"D-FSL-1.0",
"diffmark",
"DOC",
"Dotseqn",
"DRL-1.0",
"DSDP",
"dvipdfm",
"ECL-1.0",
"ECL-2.0",
"eCos-2.0",
"EFL-1.0",
"EFL-2.0",
"eGenix",
"Entessa",
"EPICS",
"EPL-1.0",
"EPL-2.0",
"ErlPL-1.1",
"etalab-2.0",
"EUDatagrid",
"EUPL-1.0",
"EUPL-1.1",
"EUPL-1.2",
"Eurosym",
"Fair",
"Frameworx-1.0",
"FreeBSD-DOC",
"FreeImage",
"FSFAP",
"FSFUL",
"FSFULLR",
"FTL",
"GD",
"GFDL-1.1",
"GFDL-1.1-invariants-only",
"GFDL-1.1-invariants-or-later",
"GFDL-1.1-no-invariants-only",
"GFDL-1.1-no-invariants-or-later",
"GFDL-1.1-only",
"GFDL-1.1-or-later",
"GFDL-1.2",
"GFDL-1.2-invariants-only",
"GFDL-1.2-invariants-or-later",
"GFDL-1.2-no-invariants-only",
"GFDL-1.2-no-invariants-or-later",
"GFDL-1.2-only",
"GFDL-1.2-or-later",
"GFDL-1.3",
"GFDL-1.3-invariants-only",
"GFDL-1.3-invariants-or-later",
"GFDL-1.3-no-invariants-only",
"GFDL-1.3-no-invariants-or-later",
"GFDL-1.3-only",
"GFDL-1.3-or-later",
"Giftware",
"GL2PS",
"Glide",
"Glulxe",
"GLWTPL",
"gnuplot",
"GPL-1.0",
"GPL-1.0-only",
"GPL-1.0-or-later",
"GPL-1.0+",
"GPL-2.0",
"GPL-2.0-only",
"GPL-2.0-or-later",
"GPL-2.0-with-autoconf-exception",
"GPL-2.0-with-bison-exception",
"GPL-2.0-with-classpath-exception",
"GPL-2.0-with-font-exception",
"GPL-2.0-with-GCC-exception",
"GPL-2.0+",
"GPL-3.0",
"GPL-3.0-only",
"GPL-3.0-or-later",
"GPL-3.0-with-autoconf-exception",
"GPL-3.0-with-GCC-exception",
"GPL-3.0+",
"gSOAP-1.3b",
"HaskellReport",
"Hippocratic-2.1",
"HPND",
"HPND-sell-variant",
"HTMLTIDY",
"IBM-pibs",
"ICU",
"IJG",
"ImageMagick",
"iMatix",
"Imlib2",
"Info-ZIP",
"Intel",
"Intel-ACPI",
"Interbase-1.0",
"IPA",
"IPL-1.0",
"ISC",
"JasPer-2.0",
"JPNIC",
"JSON",
"LAL-1.2",
"LAL-1.3",
"Latex2e",
"Leptonica",
"LGPL-2.0",
"LGPL-2.0-only",
"LGPL-2.0-or-later",
"LGPL-2.0+",
"LGPL-2.1",
"LGPL-2.1-only",
"LGPL-2.1-or-later",
"LGPL-2.1+",
"LGPL-3.0",
"LGPL-3.0-only",
"LGPL-3.0-or-later",
"LGPL-3.0+",
"LGPLLR",
"Libpng",
"libpng-2.0",
"libselinux-1.0",
"libtiff",
"LiLiQ-P-1.1",
"LiLiQ-R-1.1",
"LiLiQ-Rplus-1.1",
"Linux-OpenIB",
"LPL-1.0",
"LPL-1.02",
"LPPL-1.0",
"LPPL-1.1",
"LPPL-1.2",
"LPPL-1.3a",
"LPPL-1.3c",
"MakeIndex",
"MirOS",
"MIT",
"MIT-0",
"MIT-advertising",
"MIT-CMU",
"MIT-enna",
"MIT-feh",
"MIT-Modern-Variant",
"MIT-open-group",
"MITNFA",
"Motosoto",
"mpich2",
"MPL-1.0",
"MPL-1.1",
"MPL-2.0",
"MPL-2.0-no-copyleft-exception",
"MS-PL",
"MS-RL",
"MTLL",
"MulanPSL-1.0",
"MulanPSL-2.0",
"Multics",
"Mup",
"NAIST-2003",
"NASA-1.3",
"Naumen",
"NBPL-1.0",
"NCGL-UK-2.0",
"NCSA",
"Net-SNMP",
"NetCDF",
"Newsletr",
"NGPL",
"NIST-PD",
"NIST-PD-fallback",
"NLOD-1.0",
"NLOD-2.0",
"NLPL",
"Nokia",
"NOSL",
"Noweb",
"NPL-1.0",
"NPL-1.1",
"NPOSL-3.0",
"NRL",
"NTP",
"NTP-0",
"Nunit",
"O-UDA-1.0",
"OCCT-PL",
"OCLC-2.0",
"ODbL-1.0",
"ODC-By-1.0",
"OFL-1.0",
"OFL-1.0-no-RFN",
"OFL-1.0-RFN",
"OFL-1.1",
"OFL-1.1-no-RFN",
"OFL-1.1-RFN",
"OGC-1.0",
"OGDL-Taiwan-1.0",
"OGL-Canada-2.0",
"OGL-UK-1.0",
"OGL-UK-2.0",
"OGL-UK-3.0",
"OGTSL",
"OLDAP-1.1",
"OLDAP-1.2",
"OLDAP-1.3",
"OLDAP-1.4",
"OLDAP-2.0",
"OLDAP-2.0.1",
"OLDAP-2.1",
"OLDAP-2.2",
"OLDAP-2.2.1",
"OLDAP-2.2.2",
"OLDAP-2.3",
"OLDAP-2.4",
"OLDAP-2.5",
"OLDAP-2.6",
"OLDAP-2.7",
"OLDAP-2.8",
"OML",
"OpenSSL",
"OPL-1.0",
"OPUBL-1.0",
"OSET-PL-2.1",
"OSL-1.0",
"OSL-1.1",
"OSL-2.0",
"OSL-2.1",
"OSL-3.0",
"Parity-6.0.0",
"Parity-7.0.0",
"PDDL-1.0",
"PHP-3.0",
"PHP-3.01",
"Plexus",
"PolyForm-Noncommercial-1.0.0",
"PolyForm-Small-Business-1.0.0",
"PostgreSQL",
"PSF-2.0",
"psfrag",
"psutils",
"Python-2.0",
"Qhull",
"QPL-1.0",
"Rdisc",
"RHeCos-1.1",
"RPL-1.1",
"RPL-1.5",
"RPSL-1.0",
"RSA-MD",
"RSCPL",
"Ruby",
"SAX-PD",
"Saxpath",
"SCEA",
"Sendmail",
"Sendmail-8.23",
"SGI-B-1.0",
"SGI-B-1.1",
"SGI-B-2.0",
"SHL-0.5",
"SHL-0.51",
"SimPL-2.0",
"SISSL",
"SISSL-1.2",
"Sleepycat",
"SMLNJ",
"SMPPL",
"SNIA",
"Spencer-86",
"Spencer-94",
"Spencer-99",
"SPL-1.0",
"SSH-OpenSSH",
"SSH-short",
"SSPL-1.0",
"StandardML-NJ",
"SugarCRM-1.1.3",
"SWL",
"TAPR-OHL-1.0",
"TCL",
"TCP-wrappers",
"TMate",
"TORQUE-1.1",
"TOSL",
"TU-Berlin-1.0",
"TU-Berlin-2.0",
"UCL-1.0",
"Unicode-DFS-2015",
"Unicode-DFS-2016",
"Unicode-TOU",
"Unlicense",
"UPL-1.0",
"Verbatim-man-pages",
"Vim",
"VOSTROM",
"VSL-1.0",
"W3C",
"W3C-19980720",
"W3C-20150513",
"Watcom-1.0",
"Wsuipa",
"WTFPL",
"wxWindows",
"X11",
"Xerox",
"XFree86-1.1",
"xinetd",
"Xnet",
"xpp",
"XSkat",
"YPL-1.0",
"YPL-1.1",
"Zed",
"Zend-2.0",
"Zimbra-1.3",
"Zimbra-1.4",
"Zlib",
"zlib-acknowledgement",
"ZPL-1.1",
"ZPL-2.0",
"ZPL-2.1",