The other day I was playing a Castlevania on my RetroPie. In between the doses of pure childhood nostalgia, I found myself scrolling through the Raspberry Pi menus to find an open-source (OSS) attribution display menu. In front of me was a list of every technology used to bring this excellent piece of technology to bear. At that moment, being a developer, the idea of attribution is rarely a topic I take seriously enough, especially under the crunch of deadlines, production deployments, and endless meetings. But maybe we could all do better? How could we do that?

The .NET programming paradigm has changed for the majority of folks in our ecosystem. Developers no longer have to rely on Microsoft or third-party commercial vendors to solve mission-critical problems exclusively. At our disposal are over 231,390 unique NuGet packages and rising. With all good changes comes a wave of emergent issues that need addressing to allow the community to flourish and thrive. The case of attribution and license display help give authors and OSS contributors the credit they deserve. In a way, it also offers potential consumers awareness and confidence with existing community projects.

In this post, we’ll explore a suggestion to help developers do the right thing and make attribution easier for everyone moving forward.

What Is Attribution

Attribution is the act of providing credit to the original authors of an open-source library or package. Most popular licenses, including MIT, Apache, and GPL, have clauses requiring attribution. Here is a clause from the Apache 2.0 license, under the Redistribution section.

You must give any other recipients of the Work or Derivative Works a copy of this License Apache 2.0

In a legal phrase, collating all licenses within a logical project can be referred to as either “attribution statements” or “open source notices”. The act of collecting, processing, and presenting such a document can be a pain on multiple levels. The first issue arises with actually collecting the applicable license for the current software version one is using. The medium in which we publish software presents a challenge: web, mobile, native, or embedded. The rapid change of dependencies can make attribution statements outdated and irrelevant. Let’s be honest if it needs to be done manually by a person; it’s likely going to incomplete or ignored altogether.

The Basics

.NET has a lot of historical baggage, and while the broader ecosystem has enjoyed OSS for over a decade now, much of the technology stack hasn’t made the mental shift to embrace its still-fledgling creators. Let’s look at another community for inspiration and see what we can learn from an ecosystem built from a foundation of OSS principles; let’s take a look at Node.

Starting a new project with npm init, the console will ask every developer the following questions:

  1. The package name
  2. The starting version number of the package
  3. A description of the package
  4. Entry point
  5. Test Command
  6. Git Repository
  7. Keywords
  8. Author
  9. License

The Node community accepts that licensing and attribution are core to their ecosystem, even building tooling around generating attribution statements. For example, here is a package named oss-attribution-generator, which will parse bower and npm packages used in a project and generate an attribution file to include in a product.

Licensing and attribution aren’t vague ideals or pleasantry spoken of in Node circles; instead, that development community has actualized the idea into tooling and procedures.

So how does the .NET community do this?

Codifying Licenses and Attribution

The shareable unit within the .NET ecosystem is an assembly. We need to take steps in uniformly embedding information into assemblies to make the act of attribution as easy as possible for all consumers. To accomplish the goal, we need either of two things to occur.

First-class Support In .NET

In my opinion, the ideal scenario is for .NET to adopt new constructs in the csproj file of a project that compiles directly into the project artifacts. Let’s look at an example draft, and by no means would this be final.

<Project Sdk="Microsoft.NET.Sdk">
    <PropertyGroup>
        <OutputType>Exe</OutputType>
        <TargetFramework>net5.0</TargetFramework>
        <PackageVersion>1.0.0</PackageVersion>
        <PackageId>Some Package</PackageId>
        <Authors>Khalid Abuhakmeh</Authors>
    </PropertyGroup>
    <PropertyGroup>
        <Attribution>
            <Name UsePackageId="true"/>
            <Version UsePackageVersion="true" />
            <License>MIT</License>
            <Authors UsePackageAuthors="true" />
            <Url>https://khalidabuhakmeh.com</Url>
            <LicenseText File="LICENSE.txt"/>
        </Attribution>
    </PropertyGroup>
</Project>

The csproj format would include new elements that the compiler could read and embed directly into assemblies. There is overlap with NuGet tags like Authors, PackageId, and PackageVersion. The generated class generated within each assembly would look like the following class.

public class LicenseInformation 
    : System.Attribution.ILicenseInformation
{
    public string License { get; init; } = "MIT";
    public string LicenseText { get; init; }
    public Uri Url { get; init; }
    public string CopyrightStatement { get; init; }
    public string Version { get; init; }
    public string[] Authors { get; set; }
}

The objects are hydrated by the runtime when the developer requests them.

.NET Foundation Community Package

The .NET Foundation’s mission is to support an innovative, commercially friendly, open-source ecosystem around the .NET Platform. A shared community dependency that could generate and create these embedded classes could be a great starting point to help in that mission.

With the addition of Source Generators to the .NET toolbox, many projects could avoid shipping the dependency all together and generate similar looking classes with a shared structure. It’s critical to adoption that all instances share a similar if not exact form. We’ll see why this is important in the next few sections.

Why Not Assembly Attributes?

You might be asking, why not use Assembly attributes? Assembly attributes provide information about a particular assembly. These attributes convey information within the following categories: Assembly identity, informational attributes, assembly manifest attributes, and strong naming. For this use-case, the most relevant being informational attributes.

In the category of Informational Attributes we have the following values:

  • AssemblyCompanyAttribute: The company’s name.
  • AssemblyCopyrightAttribute: The company’s copyright information.
  • AssemblyFileVersion: The Win32 file version number.
  • AssemblyInformationalVersionAttribute: String representation of the product version number.
  • AssemblyProductAttribute: Product Information.
  • AssemblyTrademarkAttribute: The trademark for the product.

If it’s not immediately apparent, these attributes scream LEGACY!. They come from an era where .NET focused on building commercial products to ship on Windows devices. We’re a long way from that era, where today, .NET runs on many devices with varying OSes. While Assembly attributes may still have their place, OSS attribution is not a great fit.

Building On The Basics

So let’s fast-forward to a utopian future with flying cars, scientists have cured all diseases, and all .NET assemblies have an embedded attribution class. What can we do with these attribution classes?

Generate Attribution Statement

The first obvious use-case is to generate attribution statements to ship with projects and give our army of corporate lawyers. By scanning our solution, we could develop a complete snapshot and have information about our applications’ makeup. Generating the Attribution statement is a matter of running a single line of code.

// get a string of attributions scanned from assemblies
var string = System.Attribution.AttributionStatment.Generate();
// get a list of licenses from assemblies 
var licenses = System.Attribution.Licenses.All();

Wouldn’t that be great?

Analyzers / Warnings

Outside of giving credit where credit is due, developers could use these code constructs to determine if they’re within compliance with their OSS policies. A common mistake developers make is using LGPL packages in proprietary software, thus incurring significant legal fees and restitution costs. Having classes embedded with this information could catch these errors locally during development or in continuous integration environments.

Reusable Display Components

Once you’ve seen one attribution statement, you’ve likely seen them all. Why reinvent the display element when the community could build reusable components for different mediums? Here are a few imagined components:

  • A Razor Page with collapsed regions
  • A Xamarin.Forms XAML view
  • A Console command to list licenses and projects used
  • ASP.NET Middleware for Web API endpoints

These components could help standardize how we display statements and where users can expect to find attribution statements.

Conclusions

When it comes to OSS, we all likely consume more than we contribute back. One of the most straightforward changes is to attribute adequately and give credit to the authors of packages. By building attribution into the .NET Framework itself, we can automate the process and make it part of our ecosystem’s foundation, rather than relying on folks doing the “right thing”. While not impossible to be a grassroots effort from the community, this approach’s adoption would mean more coming from .NET as a technology or an institution like the .NET Foundation. Until then, we could all do better scrounging GitHub repositories for License.md files and concatenating large text blobs together.

Let me know in the comments what you think and what are some issues you see with this approach?

As always, thanks for reading, and feel free to share this with folks.

References