Skip to content

Commit

Permalink
Add basic support for license expressions
Browse files Browse the repository at this point in the history
  • Loading branch information
sensslen committed Mar 13, 2024
1 parent 2c618a3 commit 7560a72
Show file tree
Hide file tree
Showing 32 changed files with 1,253 additions and 73 deletions.
8 changes: 4 additions & 4 deletions .github/workflows/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ jobs:
strategy:
matrix:
targetFramework: [net6.0, net7.0, net8.0]
project: [App, Tests]
project: [App, Tests, ProjectWithReferenceContainingLicenseExpression]

include:
- targetFramework: net6.0
Expand All @@ -110,7 +110,7 @@ jobs:
dotnet-version: ${{ matrix.dotnetVersion }}

- name: restore
run: dotnet restore -p:TargetFramework=${{ matrix.targetFramework }}
run: dotnet restore NuGetUtility.sln

- name: build
run: dotnet publish ./src/NuGetLicenseCore/NuGetLicenseCore.csproj --configuration Release -o ./release -f ${{ matrix.targetFramework }} --no-restore
Expand All @@ -131,7 +131,7 @@ jobs:
runs-on: windows-latest
strategy:
matrix:
project: [App, Tests]
project: [App, Tests, ProjectWithReferenceContainingLicenseExpression]

steps:
- uses: actions/checkout@v4
Expand Down Expand Up @@ -195,7 +195,7 @@ jobs:
dotnet-version: ${{ matrix.dotnetVersion }}

- name: restore
run: dotnet restore -p:TargetFramework=${{ matrix.targetFramework }}
run: dotnet restore NuGetUtility.sln

- uses: paulhatch/[email protected]
id: version
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
["MIT","Apache-2.0","MS-EULA"]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
["NETStandard.Library"]
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[
]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
["./integration/ProjectWithReferenceContainingLicenseExpression/ProjectWithReferenceContainingLicenseExpression.csproj"]
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
{
}
48 changes: 46 additions & 2 deletions NuGetUtility.sln
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,15 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SimpleCppProject", "tests\targets\SimpleCppProject\SimpleCppProject.vcxproj", "{380FBD90-2CF0-4F83-A58E-EB98CE2EAE15}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NuGetLicenseCore", "src\NuGetLicenseCore\NuGetLicenseCore.csproj", "{FBA6622A-C9E3-4250-AB79-35F02CAD2419}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NuGetLicenseCore", "src\NuGetLicenseCore\NuGetLicenseCore.csproj", "{FBA6622A-C9E3-4250-AB79-35F02CAD2419}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NuGetLicenseFramework", "src\NuGetLicenseFramework\NuGetLicenseFramework.csproj", "{DE079B9C-B6BA-4D53-8B83-03D3CBD4027F}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NuGetLicenseFramework", "src\NuGetLicenseFramework\NuGetLicenseFramework.csproj", "{DE079B9C-B6BA-4D53-8B83-03D3CBD4027F}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tethys.SPDX.ExpressionParser", "src\Tethys.SPDX.ExpressionParser\Tethys.SPDX.ExpressionParser.csproj", "{408005E7-D628-477C-A816-59AA4AD3E40C}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "integration", "integration", "{FFB2826C-17A3-4C18-8FFE-A34AB51592F7}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ProjectWithReferenceContainingLicenseExpression", "integration\ProjectWithReferenceContainingLicenseExpression\ProjectWithReferenceContainingLicenseExpression.csproj", "{01704839-219A-4CE2-93F4-DB3CB8773DCE}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand Down Expand Up @@ -224,6 +230,42 @@ Global
{DE079B9C-B6BA-4D53-8B83-03D3CBD4027F}.TestWindows|x64.Build.0 = Debug|Any CPU
{DE079B9C-B6BA-4D53-8B83-03D3CBD4027F}.TestWindows|x86.ActiveCfg = Debug|Any CPU
{DE079B9C-B6BA-4D53-8B83-03D3CBD4027F}.TestWindows|x86.Build.0 = Debug|Any CPU
{408005E7-D628-477C-A816-59AA4AD3E40C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{408005E7-D628-477C-A816-59AA4AD3E40C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{408005E7-D628-477C-A816-59AA4AD3E40C}.Debug|x64.ActiveCfg = Debug|Any CPU
{408005E7-D628-477C-A816-59AA4AD3E40C}.Debug|x64.Build.0 = Debug|Any CPU
{408005E7-D628-477C-A816-59AA4AD3E40C}.Debug|x86.ActiveCfg = Debug|Any CPU
{408005E7-D628-477C-A816-59AA4AD3E40C}.Debug|x86.Build.0 = Debug|Any CPU
{408005E7-D628-477C-A816-59AA4AD3E40C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{408005E7-D628-477C-A816-59AA4AD3E40C}.Release|Any CPU.Build.0 = Release|Any CPU
{408005E7-D628-477C-A816-59AA4AD3E40C}.Release|x64.ActiveCfg = Release|Any CPU
{408005E7-D628-477C-A816-59AA4AD3E40C}.Release|x64.Build.0 = Release|Any CPU
{408005E7-D628-477C-A816-59AA4AD3E40C}.Release|x86.ActiveCfg = Release|Any CPU
{408005E7-D628-477C-A816-59AA4AD3E40C}.Release|x86.Build.0 = Release|Any CPU
{408005E7-D628-477C-A816-59AA4AD3E40C}.TestWindows|Any CPU.ActiveCfg = Debug|Any CPU
{408005E7-D628-477C-A816-59AA4AD3E40C}.TestWindows|Any CPU.Build.0 = Debug|Any CPU
{408005E7-D628-477C-A816-59AA4AD3E40C}.TestWindows|x64.ActiveCfg = Debug|Any CPU
{408005E7-D628-477C-A816-59AA4AD3E40C}.TestWindows|x64.Build.0 = Debug|Any CPU
{408005E7-D628-477C-A816-59AA4AD3E40C}.TestWindows|x86.ActiveCfg = Debug|Any CPU
{408005E7-D628-477C-A816-59AA4AD3E40C}.TestWindows|x86.Build.0 = Debug|Any CPU
{01704839-219A-4CE2-93F4-DB3CB8773DCE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{01704839-219A-4CE2-93F4-DB3CB8773DCE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{01704839-219A-4CE2-93F4-DB3CB8773DCE}.Debug|x64.ActiveCfg = Debug|Any CPU
{01704839-219A-4CE2-93F4-DB3CB8773DCE}.Debug|x64.Build.0 = Debug|Any CPU
{01704839-219A-4CE2-93F4-DB3CB8773DCE}.Debug|x86.ActiveCfg = Debug|Any CPU
{01704839-219A-4CE2-93F4-DB3CB8773DCE}.Debug|x86.Build.0 = Debug|Any CPU
{01704839-219A-4CE2-93F4-DB3CB8773DCE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{01704839-219A-4CE2-93F4-DB3CB8773DCE}.Release|Any CPU.Build.0 = Release|Any CPU
{01704839-219A-4CE2-93F4-DB3CB8773DCE}.Release|x64.ActiveCfg = Release|Any CPU
{01704839-219A-4CE2-93F4-DB3CB8773DCE}.Release|x64.Build.0 = Release|Any CPU
{01704839-219A-4CE2-93F4-DB3CB8773DCE}.Release|x86.ActiveCfg = Release|Any CPU
{01704839-219A-4CE2-93F4-DB3CB8773DCE}.Release|x86.Build.0 = Release|Any CPU
{01704839-219A-4CE2-93F4-DB3CB8773DCE}.TestWindows|Any CPU.ActiveCfg = TestWindows|Any CPU
{01704839-219A-4CE2-93F4-DB3CB8773DCE}.TestWindows|Any CPU.Build.0 = TestWindows|Any CPU
{01704839-219A-4CE2-93F4-DB3CB8773DCE}.TestWindows|x64.ActiveCfg = TestWindows|Any CPU
{01704839-219A-4CE2-93F4-DB3CB8773DCE}.TestWindows|x64.Build.0 = TestWindows|Any CPU
{01704839-219A-4CE2-93F4-DB3CB8773DCE}.TestWindows|x86.ActiveCfg = TestWindows|Any CPU
{01704839-219A-4CE2-93F4-DB3CB8773DCE}.TestWindows|x86.Build.0 = TestWindows|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -240,6 +282,8 @@ Global
{380FBD90-2CF0-4F83-A58E-EB98CE2EAE15} = {FA92392F-D895-4D1E-A5ED-E6DC3C08223E}
{FBA6622A-C9E3-4250-AB79-35F02CAD2419} = {D2AB2D00-1F48-487D-BFE0-99FDB4E071CC}
{DE079B9C-B6BA-4D53-8B83-03D3CBD4027F} = {D2AB2D00-1F48-487D-BFE0-99FDB4E071CC}
{408005E7-D628-477C-A816-59AA4AD3E40C} = {D2AB2D00-1F48-487D-BFE0-99FDB4E071CC}
{01704839-219A-4CE2-93F4-DB3CB8773DCE} = {FFB2826C-17A3-4C18-8FFE-A34AB51592F7}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {70887D40-0182-4C32-BFA1-B5A02E405F11}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<Configurations>Debug;Release;TestWindows</Configurations>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="CsvHelper" Version="31.0.2" />
</ItemGroup>

</Project>
1 change: 1 addition & 0 deletions src/NuGetLicenseCore/NuGetLicenseCore.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
<Platforms>AnyCPU</Platforms>
<PackageReadmeFile>README.md</PackageReadmeFile>
<Description>A .net tool to print and validate the licenses of .net code. This tool supports .NET (Core), .NET Standard and .NET Framework projects.</Description>
<PackageTags>NuGet;License</PackageTags>
</PropertyGroup>

<ItemGroup>
Expand Down
12 changes: 11 additions & 1 deletion src/NuGetUtility/LicenseValidator/LicenseValidator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using NuGetUtility.Wrapper.NuGetWrapper.Packaging;
using NuGetUtility.Wrapper.NuGetWrapper.Packaging.Core;
using NuGetUtility.Wrapper.NuGetWrapper.Versioning;
using Tethys.SPDX.ExpressionParser;

namespace NuGetUtility.LicenseValidator
{
Expand Down Expand Up @@ -126,7 +127,8 @@ private void ValidateLicenseByMetadata(IPackageMetadata info,
case LicenseType.Expression:
case LicenseType.Overwrite:
string licenseId = info.LicenseMetadata!.License;
if (IsLicenseValid(licenseId))
SpdxExpression? licenseExpression = SpdxExpressionParser.Parse(licenseId, _ => true, _ => true);
if (IsValidLicenseExpression(licenseExpression))
{
AddOrUpdateLicense(result,
info,
Expand Down Expand Up @@ -154,6 +156,14 @@ private void ValidateLicenseByMetadata(IPackageMetadata info,
}
}

private bool IsValidLicenseExpression(SpdxExpression? expression) => expression switch
{
SpdxAndExpression and => IsValidLicenseExpression(and.Left) && IsValidLicenseExpression(and.Right),
SpdxOrExpression or => IsValidLicenseExpression(or.Left) || IsValidLicenseExpression(or.Right),
SpdxWithExpression or SpdxLicenseExpression or SpdxLicenseReference => IsLicenseValid(expression.ToString()),
_ => false,
};

private async Task ValidateLicenseByUrl(IPackageMetadata info,
string context,
ConcurrentDictionary<LicenseNameAndVersion, LicenseValidationResult> result,
Expand Down
56 changes: 29 additions & 27 deletions src/NuGetUtility/LicenseValidator/UrlToLicenseMapping.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,37 +17,39 @@ public static class UrlToLicenseMapping
public static IImmutableDictionary<Uri, string> Default { get; } = ImmutableDictionary.CreateRange(
new[]
{
new KeyValuePair<Uri,string>( new Uri("http://www.apache.org/licenses/LICENSE-2.0.html"), Apache20 ),
new KeyValuePair<Uri,string>( new Uri("http://www.apache.org/licenses/LICENSE-2.0"), Apache20 ),
new KeyValuePair<Uri,string>( new Uri("http://aws.amazon.com/apache2.0/"), Apache20 ),
new KeyValuePair<Uri,string>( new Uri("https://github.com/owin-contrib/owin-hosting/blob/master/LICENSE.txt"), Apache20 ),
new KeyValuePair<Uri,string>( new Uri("https://raw.githubusercontent.com/aspnet/Home/2.0.0/LICENSE.txt"), Apache20 ),
new KeyValuePair<Uri,string>(
new Uri("https://github.com/Microsoft/Microsoft.IO.RecyclableMemoryStream/blob/master/LICENSE"), Mit
new KeyValuePair<Uri, string>(new Uri("http://www.apache.org/licenses/LICENSE-2.0.html"), Apache20),
new KeyValuePair<Uri, string>(new Uri("http://www.apache.org/licenses/LICENSE-2.0"), Apache20),
new KeyValuePair<Uri, string>(new Uri("http://aws.amazon.com/apache2.0/"), Apache20),
new KeyValuePair<Uri, string>(new Uri("https://github.com/owin-contrib/owin-hosting/blob/master/LICENSE.txt"), Apache20),
new KeyValuePair<Uri, string>(new Uri("https://raw.githubusercontent.com/aspnet/Home/2.0.0/LICENSE.txt"), Apache20),
new KeyValuePair<Uri, string>(
new Uri("https://github.com/Microsoft/Microsoft.IO.RecyclableMemoryStream/blob/master/LICENSE"),
Mit
),
new KeyValuePair<Uri,string>( new Uri("https://github.com/AutoMapper/AutoMapper/blob/master/LICENSE.txt"), Mit ),
new KeyValuePair<Uri,string>( new Uri("https://github.com/zzzprojects/html-agility-pack/blob/master/LICENSE"), Mit ),
new KeyValuePair<Uri,string>( new Uri("https://raw.githubusercontent.com/hey-red/markdownsharp/master/LICENSE"), Mit ),
new KeyValuePair<Uri,string>( new Uri("https://raw.github.com/JamesNK/Newtonsoft.Json/master/LICENSE.md"), Mit ),
new KeyValuePair<Uri,string>( new Uri("https://licenses.nuget.org/MIT"), Mit ),
new KeyValuePair<Uri,string>( new Uri("http://max.mit-license.org/"), Mit ),
new KeyValuePair<Uri,string>( new Uri("https://github.com/dotnet/corefx/blob/master/LICENSE.TXT"), Mit ),
new KeyValuePair<Uri,string>( new Uri("https://go.microsoft.com/fwlink/?linkid=868514"), Mit ),
new KeyValuePair<Uri,string>( new Uri("http://go.microsoft.com/fwlink/?linkid=833178"), Mit ),
new KeyValuePair<Uri,string>( new Uri("http://www.gnu.org/licenses/old-licenses/gpl-2.0.html"), Gpl20 ),
new KeyValuePair<Uri,string>( new Uri("https://raw.githubusercontent.com/AArnott/Validation/8377954d86/LICENSE.txt"), MsPl ),
new KeyValuePair<Uri,string>( new Uri("https://www.microsoft.com/web/webpi/eula/aspnetmvc3update-eula.htm"), MsEula ),
new KeyValuePair<Uri,string>( new Uri("http://go.microsoft.com/fwlink/?LinkID=214339"), MsEula ),
new KeyValuePair<Uri,string>( new Uri("https://www.microsoft.com/web/webpi/eula/net_library_eula_enu.htm"), MsEula ),
new KeyValuePair<Uri,string>( new Uri("http://go.microsoft.com/fwlink/?LinkId=329770"), MsEula ),
new KeyValuePair<Uri,string>( new Uri("http://go.microsoft.com/fwlink/?LinkId=529443"), MsEula ),
new KeyValuePair<Uri,string>(
new KeyValuePair<Uri, string>(new Uri("https://github.com/AutoMapper/AutoMapper/blob/master/LICENSE.txt"), Mit),
new KeyValuePair<Uri, string>(new Uri("https://github.com/zzzprojects/html-agility-pack/blob/master/LICENSE"), Mit),
new KeyValuePair<Uri, string>(new Uri("https://raw.githubusercontent.com/hey-red/markdownsharp/master/LICENSE"), Mit),
new KeyValuePair<Uri, string>(new Uri("https://raw.github.com/JamesNK/Newtonsoft.Json/master/LICENSE.md"), Mit),
new KeyValuePair<Uri, string>(new Uri("https://licenses.nuget.org/MIT"), Mit),
new KeyValuePair<Uri, string>(new Uri("http://max.mit-license.org/"), Mit),
new KeyValuePair<Uri, string>(new Uri("https://github.com/dotnet/corefx/blob/master/LICENSE.TXT"), Mit),
new KeyValuePair<Uri, string>(new Uri("https://go.microsoft.com/fwlink/?linkid=868514"), Mit),
new KeyValuePair<Uri, string>(new Uri("http://go.microsoft.com/fwlink/?linkid=833178"), Mit),
new KeyValuePair<Uri, string>(new Uri("http://www.gnu.org/licenses/old-licenses/gpl-2.0.html"), Gpl20),
new KeyValuePair<Uri, string>(new Uri("https://raw.githubusercontent.com/AArnott/Validation/8377954d86/LICENSE.txt"), MsPl),
new KeyValuePair<Uri, string>(new Uri("https://www.microsoft.com/web/webpi/eula/aspnetmvc3update-eula.htm"), MsEula),
new KeyValuePair<Uri, string>(new Uri("http://go.microsoft.com/fwlink/?LinkID=214339"), MsEula),
new KeyValuePair<Uri, string>(new Uri("https://www.microsoft.com/web/webpi/eula/net_library_eula_enu.htm"), MsEula),
new KeyValuePair<Uri, string>(new Uri("http://go.microsoft.com/fwlink/?LinkId=329770"), MsEula),
new KeyValuePair<Uri, string>(new Uri("http://go.microsoft.com/fwlink/?LinkId=529443"), MsEula),
new KeyValuePair<Uri, string>(
new Uri("https://www.microsoft.com/web/webpi/eula/dotnet_library_license_non_redistributable.htm"),
MsEulaNonRedistributable
),
new KeyValuePair<Uri,string>( new Uri("http://go.microsoft.com/fwlink/?LinkId=529444"), MsEulaNonRedistributable ),
new KeyValuePair<Uri,string>( new Uri(" http://opensource.org/licenses/mit-license.php"), Mit ),
new KeyValuePair<Uri,string>( new Uri("https://raw.githubusercontent.com/bchavez/Bogus/master/LICENSE"), Mit)
new KeyValuePair<Uri, string>(new Uri("http://go.microsoft.com/fwlink/?LinkId=529444"), MsEulaNonRedistributable),
new KeyValuePair<Uri, string>(new Uri(" http://opensource.org/licenses/mit-license.php"), Mit),
new KeyValuePair<Uri, string>(new Uri("https://raw.githubusercontent.com/bchavez/Bogus/master/LICENSE"), Mit),
new KeyValuePair<Uri, string>(new Uri("https://github.com/Microsoft/dotnet/blob/master/LICENSE"), Mit)
}
);
}
Expand Down
6 changes: 5 additions & 1 deletion src/NuGetUtility/NuGetUtility.csproj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>net472;net6.0;net7.0;net8.0</TargetFrameworks>
Expand Down Expand Up @@ -60,4 +60,8 @@
</Content>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Tethys.SPDX.ExpressionParser\Tethys.SPDX.ExpressionParser.csproj" />
</ItemGroup>

</Project>
53 changes: 53 additions & 0 deletions src/Tethys.SPDX.ExpressionParser/SpdxAndExpression.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// Licensed to the projects contributors.
// The license conditions are provided in the LICENSE file located in the project root

using System;

namespace Tethys.SPDX.ExpressionParser
{
/// <summary>
/// Represents an SPDX expression.
/// </summary>
public class SpdxAndExpression : SpdxExpression
{
//// Annex D SPDX license expressions
//// compound-expression "AND" compound-expression

#region PUBLIC PROPERTIES
/// <summary>
/// Gets the left side of the expression.
/// </summary>
public SpdxExpression Left { get; }

/// <summary>
/// Gets the right side of the expression.
/// </summary>
public SpdxExpression Right { get; }
#endregion // PUBLIC PROPERTIES

//// ---------------------------------------------------------------------

#region CONSTRUCTION
/// <summary>
/// Initializes a new instance of the <see cref="SpdxAndExpression"/> class.
/// </summary>
/// <param name="left">The left.</param>
/// <param name="right">The right.</param>
public SpdxAndExpression(SpdxExpression? left, SpdxExpression? right)
{
Left = left ?? throw new ArgumentNullException(nameof(left));
Right = right ?? throw new ArgumentNullException(nameof(right));
} // SpdxAndExpression()
#endregion // CONSTRUCTION

//// ---------------------------------------------------------------------

#region PUBLIC METHODS
/// <inheritdoc />
public override string ToString()
{
return $"{Left.ToString()} AND {Right.ToString()}";
} // ToString()
#endregion // PUBLIC METHODS
} // SpdxAndExpression
}
42 changes: 42 additions & 0 deletions src/Tethys.SPDX.ExpressionParser/SpdxExpression.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Licensed to the projects contributors.
// The license conditions are provided in the LICENSE file located in the project root

namespace Tethys.SPDX.ExpressionParser
{
/*************************************************************************
* SPDX Expressions
* ----------------
* idstring = 1*(ALPHA / DIGIT / "-" / "." )
*
* license-id = <short form license identifier in Annex A.1>
*
* license-exception-id = <short form license exception identifier in Annex A.2>
*
* license-ref = ["DocumentRef-"(idstring)":"]"LicenseRef-"(idstring)
*
* simple-expression = license-id / license-id"+" / license-ref
*
* compound-expression = (simple-expression /
* simple-expression "WITH" license-exception-id /
* compound-expression "AND" compound-expression /
* compound-expression "OR" compound-expression /
* "(" compound-expression ")" )
*
* license-expression = (simple-expression / compound-expression)
*
************************************************************************/

/// <summary>
/// Represents an SPDX expression.
/// </summary>
public abstract class SpdxExpression
{
/// <summary>
/// Converts an <see cref="SpdxExpression"/> to a string.
/// </summary>
/// <returns>
/// A <see cref="string" /> that represents this instance.
/// </returns>
public new abstract string ToString();
} // SpdxExpression
}
Loading

0 comments on commit 7560a72

Please sign in to comment.