diff --git a/.github/workflows/golang.yml b/.github/workflows/golang.yml index 8e2815b..5fc529d 100644 --- a/.github/workflows/golang.yml +++ b/.github/workflows/golang.yml @@ -78,3 +78,19 @@ jobs: with: go-version: ${{ env.GOLANG_VERSION }} - run: make build + third-party-notices: + name: Check third-party notices + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + name: Checkout code + - name: Get Golang version + id: vars + run: | + GOLANG_VERSION=$( grep "GOLANG_VERSION ?=" versions.mk ) + echo "GOLANG_VERSION=${GOLANG_VERSION##GOLANG_VERSION ?= }" >> $GITHUB_ENV + - name: Install Go + uses: actions/setup-go@v6 + with: + go-version: ${{ env.GOLANG_VERSION }} + - run: make check-third-party-notices diff --git a/.gitignore b/.gitignore index 0eb7e8c..d79c4e6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ *.swp *.swo *.test +/bin/ diff --git a/Makefile b/Makefile index 8c0984d..04eff96 100644 --- a/Makefile +++ b/Makefile @@ -19,7 +19,7 @@ DOCKER ?= docker PCI_IDS_URL ?= https://pci-ids.ucw.cz/v2.2/pci.ids CHECK_TARGETS := lint -TARGETS := binary build all check fmt assert-fmt generate lint vet test coverage +TARGETS := binary build all check fmt assert-fmt generate lint vet test coverage third-party-notices check-third-party-notices DOCKER_TARGETS := $(patsubst %,docker-%, $(TARGETS)) .PHONY: $(TARGETS) $(DOCKER_TARGETS) vendor check-vendor @@ -84,6 +84,21 @@ coverage: test update-pcidb: wget $(PCI_IDS_URL) -O $(CURDIR)/pkg/pciids/default_pci.ids +GO_LICENSES = $(CURDIR)/bin/go-licenses + +$(GO_LICENSES): versions.mk + GOBIN=$(CURDIR)/bin GOFLAGS=-mod=mod go install github.com/google/go-licenses/v2@$(GO_LICENSES_VERSION) + +third-party-notices: $(GO_LICENSES) + @bash hack/generate-third-party-notices.sh + +check-third-party-notices: third-party-notices + @echo "- Checking if THIRD_PARTY_NOTICES.md is up to date..." + @git ls-files --error-unmatch THIRD_PARTY_NOTICES.md >/dev/null 2>&1 \ + || { echo "ERROR: THIRD_PARTY_NOTICES.md is not tracked. Run 'make third-party-notices' and commit the result."; exit 1; } + @git diff --exit-code -- THIRD_PARTY_NOTICES.md \ + || { echo "ERROR: THIRD_PARTY_NOTICES.md is stale. Run 'make third-party-notices' and commit the change."; exit 1; } + build-image: $(DOCKERFILE_DEVEL) $(DOCKER) build \ --progress=plain \ diff --git a/THIRD_PARTY_NOTICES.md b/THIRD_PARTY_NOTICES.md new file mode 100644 index 0000000..df79725 --- /dev/null +++ b/THIRD_PARTY_NOTICES.md @@ -0,0 +1,394 @@ +# Third-Party Notices + +NVIDIA go-nvlib + +This file lists the third-party dependencies that go-nvlib links into the +packages a consumer imports, along with the verbatim text of each dependency's +license. It covers the **Go modules** in the non-test import closure of the +packages under `pkg/`, and the third-party non-Go files committed into this +repository and compiled into those packages. + +Go standard library packages are excluded; they are covered by the license of +the Go distribution itself. NVIDIA's own code is excluded; it is covered by +`LICENSE`. Dependencies reached only from `_test.go` files are excluded; they +are vendored for testing but a consumer does not link them. + +The `NOTICE` file at the root of this repository remains part of the +distribution. Its entry for `pkg/pciids/default_pci.ids` is reproduced in full +below, so this file stands on its own. + +## Go Dependency Index + +| Package | License | Module | +|---------|---------|--------| +| `github.com/NVIDIA/go-nvml/pkg` | Apache-2.0 | `github.com/NVIDIA/go-nvml` | +| `github.com/google/uuid` | BSD-3-Clause | `github.com/google/uuid` | +| `golang.org/x/sys/unix` | BSD-3-Clause | `golang.org/x/sys` | + +## Bundled Non-Go File Index + +| File | Component | License | Source | +|------|-----------|---------|--------| +| `pkg/pciids/default_pci.ids` | PCI ID Project | BSD-3-Clause | https://pci-ids.ucw.cz/ | + +## Go Dependency License Texts + +### github.com/NVIDIA/go-nvml/pkg + +* License: Apache-2.0 +* Module: github.com/NVIDIA/go-nvml + +#### LICENSE + +```text + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +``` + + +### github.com/google/uuid + +* License: BSD-3-Clause +* Module: github.com/google/uuid + +#### LICENSE + +```text +Copyright (c) 2009,2014 Google Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +``` + + +### golang.org/x/sys/unix + +* License: BSD-3-Clause +* Module: golang.org/x/sys + +#### LICENSE + +```text +Copyright 2009 The Go Authors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google LLC nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +``` + + +## Bundled Non-Go File Notices + +### PCI ID Project + +* File: `pkg/pciids/default_pci.ids` +* License: BSD-3-Clause +* Source: https://pci-ids.ucw.cz/ +* Note: Upstream offers this database under either the GNU General Public License (version 2 or later) or the 3-clause BSD License. This project distributes it under the 3-clause BSD License, as recorded in the NOTICE file at the root of this repository. The file is embedded into the pkg/pciids package with go:embed, so it is compiled into every consumer of that package. Refresh it with 'make update-pcidb'. + +#### Notice as it appears in pkg/pciids/default_pci.ids + +```text +# +# List of PCI ID's +# +# Version: 2025.07.11 +# Date: 2025-07-11 03:15:02 +# +# Maintained by Albert Pool, Martin Mares, and other volunteers from +# the PCI ID Project at https://pci-ids.ucw.cz/. +# +# New data are always welcome, especially if they are accurate. If you have +# anything to contribute, please follow the instructions at the web site. +# +# This file can be distributed under either the GNU General Public License +# (version 2 or higher) or the 3-clause BSD License. +# +# The database is a compilation of factual data, and as such the copyright +# only covers the aggregation and formatting. The copyright is held by +# Martin Mares and Albert Pool. +# + +``` + +#### BSD-3-Clause + +```text +Copyright (c) Martin Mares and Albert Pool. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +``` + + diff --git a/hack/generate-third-party-notices.sh b/hack/generate-third-party-notices.sh new file mode 100755 index 0000000..fe65d08 --- /dev/null +++ b/hack/generate-third-party-notices.sh @@ -0,0 +1,529 @@ +#!/usr/bin/env bash +# Copyright (c) NVIDIA CORPORATION. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -euo pipefail + +OUTPUT="${OUTPUT:-THIRD_PARTY_NOTICES.md}" +MODULES_TXT="${MODULES_TXT:-vendor/modules.txt}" +ASSET_LICENSES_DIR="${ASSET_LICENSES_DIR:-hack/notices/licenses}" +NOTICE_FILE="${NOTICE_FILE:-NOTICE}" + +LICENSE_PHRASE_TO_SPDX_ID=( + "3-clause BSD License|BSD-3-Clause" + "GNU General Public License version 2|GPL-2.0-only" + "GNU General Public License version 2 or later|GPL-2.0-or-later" +) + +NOTICE_PATH_AND_PHRASE_RE='^The file ([^[:space:]]+) is distributed under the (.+)\.[[:space:]]+Maintained by ' +NOTICE_COMPONENT_AND_URL_RE='[[:space:]]from the (.+) at (https?://[^[:space:]]+)[[:space:]]*$' + +NOTICE_FORMAT_HELP=( + "" + "Describe each bundled file in ${NOTICE_FILE} as one blank-line separated paragraph:" + "" + " The file is distributed under the ." + " Maintained by from" + " the at ." + "" + "For example:" + "" + " The file pkg/pciids/default_pci.ids is distributed under the 3-clause BSD License." + " Maintained by Albert Pool, Martin Mares, and other volunteers from" + " the PCI ID Project at https://pci-ids.ucw.cz/." +) + +PACKAGE_PATTERNS=("./...") + +# Listed explicitly: go-licenses resolves one platform per run and build tags differ. +PLATFORMS=( + "linux/amd64" + "linux/arm64" + "darwin/amd64" + "darwin/arm64" +) + +die() { + printf 'ERROR: %s\n' "$1" >&2 + shift + if (( $# > 0 )); then + printf '%s\n' "$@" >&2 + fi + exit 1 +} + +log() { + printf '%s\n' "$*" >&2 +} + +# A license that is itself Markdown would close a fixed ``` fence early. +fence_for_file() { + local file="$1" longest_backtick_run fence_width + # -a: a license holding a NUL byte would print "Binary file ... matches". + longest_backtick_run=$(LC_ALL=C grep -oaE '`+' "${file}" 2>/dev/null \ + | awk '{ if (length($0) > longest) longest = length($0) } END { print longest+0 }') + fence_width=$(( longest_backtick_run + 1 )) + (( fence_width < 3 )) && fence_width=3 + printf '%*s' "${fence_width}" '' | tr ' ' '`' +} + +emit_fenced_file() { + local file="$1" fence + fence="$(fence_for_file "${file}")" + printf '%stext\n' "${fence}" + cat "${file}" + echo + printf '%s\n' "${fence}" + echo +} + +check_prerequisites() { + command -v go >/dev/null 2>&1 || die "go is not installed." + + if ./bin/go-licenses --help >/dev/null 2>&1; then + GO_LICENSES_BIN="${PWD}/bin/go-licenses" + else + die "go-licenses is not installed, or ./bin/go-licenses cannot run on this host." \ + "Delete ./bin/go-licenses if it is present, then run 'make third-party-notices';" \ + "make reinstalls it once it is gone." + fi + + local required_file + for required_file in "${MODULES_TXT}" "${NOTICE_FILE}"; do + [[ -f "${required_file}" ]] \ + || die "${required_file} not found — run 'make third-party-notices' from the repo root." + done + [[ -d "${ASSET_LICENSES_DIR}" ]] \ + || die "${ASSET_LICENSES_DIR} not found — run 'make third-party-notices' from the repo root." + + # Inside a Go workspace, go list and go-licenses resolve against every go.work module. + export GOWORK=off + + LOCAL_MODULE=$(go list -m 2>/dev/null || true) + [[ -n "${LOCAL_MODULE}" ]] || die "could not determine local module path via 'go list -m'." + [[ "${LOCAL_MODULE}" != *$'\n'* ]] \ + || die "'go list -m' reported more than one module path:" \ + "" \ + "${LOCAL_MODULE}" \ + "" \ + "This generator describes a single module; run it from the root of one." + + # CGO must stay on: with it off, build constraints exclude every file in + # go-nvml/pkg/dl and go-licenses cannot load ./... at all. + export GOFLAGS="-mod=vendor" + export CGO_ENABLED=1 +} + +prepare_workspace() { + # Explicit templates: macOS mktemp ignores TMPDIR without one. + local template_prefix="${TMPDIR:-/tmp}/go-nvlib-notices" + PLATFORM_SAVE_ROOT="$(mktemp -d "${template_prefix}.XXXXXX")" + LICENSES_DIR="$(mktemp -d "${template_prefix}-licenses.XXXXXX")" + COMBINED_CSV="$(mktemp "${template_prefix}-csv.XXXXXX")" + GO_INDEX_FILE="$(mktemp "${template_prefix}-go-index.XXXXXX")" + ASSETS_INDEX_FILE="$(mktemp "${template_prefix}-assets.XXXXXX")" + UNSORTED_ASSETS_INDEX_FILE="$(mktemp "${template_prefix}-assets-unsorted.XXXXXX")" + ASSET_LEADING_COMMENT_FILE="$(mktemp "${template_prefix}-asset-comment.XXXXXX")" + + local output_dir + output_dir="$(dirname "${OUTPUT}")" + mkdir -p "${output_dir}" + OUTPUT_TMP="$(mktemp "${output_dir}/.$(basename "${OUTPUT}").XXXXXX")" + trap 'rm -rf "${PLATFORM_SAVE_ROOT}" "${LICENSES_DIR}"; rm -f "${COMBINED_CSV}" "${GO_INDEX_FILE}" "${ASSETS_INDEX_FILE}" "${UNSORTED_ASSETS_INDEX_FILE}" "${ASSET_LEADING_COMMENT_FILE}" "${OUTPUT_TMP}"' EXIT +} + +collect_go_dependencies() { + local platform goos goarch platform_save_dir + + for platform in "${PLATFORMS[@]}"; do + goos="${platform%/*}" + goarch="${platform#*/}" + log "Collecting licenses for ${goos}/${goarch}..." + + platform_save_dir="${PLATFORM_SAVE_ROOT}/${goos}_${goarch}" + + # No --ignore: it matches raw string prefixes and drops silently, so "go" + # would take golang.org/x/* and LOCAL_MODULE a real "${LOCAL_MODULE}-extra". + GOOS="${goos}" GOARCH="${goarch}" "${GO_LICENSES_BIN}" save "${PACKAGE_PATTERNS[@]}" \ + --save_path="${platform_save_dir}" \ + --force + + GOOS="${goos}" GOARCH="${goarch}" "${GO_LICENSES_BIN}" csv "${PACKAGE_PATTERNS[@]}" \ + >> "${COMBINED_CSV}" + + cp -R "${platform_save_dir}/." "${LICENSES_DIR}/" + chmod -R u+w "${LICENSES_DIR}" + done +} + +# Join rather than pick: go-licenses emits one row per recognized license. +join_licenses_per_package() { + LC_ALL=C sort -u "$1" | awk -F, ' + { + package_path = $1 + if (!(package_path in source_url)) { + source_url[package_path] = $2 + package_order[++package_count] = package_path + } + if (!((package_path SUBSEP $3) in seen_package_license)) { + seen_package_license[package_path SUBSEP $3] = 1 + # Count, do not test "package_path in licenses": mawk instantiates + # the target before evaluating the RHS, BWK awk does not. + licenses[package_path] = \ + (license_count[package_path]++ ? licenses[package_path] " / " : "") $3 + } + } + END { + for (i = 1; i <= package_count; i++) { + package_path = package_order[i] + print package_path "," source_url[package_path] "," licenses[package_path] + } + } + ' +} + +drop_local_module() { + awk -F, -v local_module="${LOCAL_MODULE}" ' + $1 == local_module || index($1, local_module "/") == 1 { next } + { print } + ' +} + +# Module path, not the URL go-licenses reports: in vendor mode that points into +# this repo at HEAD and stops describing released content once main moves. +append_module_path() { + awk -v modules_txt="${MODULES_TXT}" ' + BEGIN { + FS = OFS = "," + while ((getline line < modules_txt) > 0) { + if (line !~ /^# /) continue + split(line, fields, " ") + if (fields[4] == "=>" || fields[3] == "=>") { + replacement_path_field = (fields[4] == "=>") ? 5 : 4 + if (fields[replacement_path_field + 1] == "") { + print "ERROR: " modules_txt " replaces " fields[2] " with a local path;" > "/dev/stderr" + print "teach hack/generate-third-party-notices.sh how to attribute it." > "/dev/stderr" + exit 1 + } + module_paths[++module_count] = fields[2] + attributed_path[fields[2]] = fields[replacement_path_field] + } else { + module_paths[++module_count] = fields[2] + attributed_path[fields[2]] = fields[2] + } + } + close(modules_txt) + if (module_count == 0) { + print "ERROR: no module lines read from " modules_txt > "/dev/stderr" + exit 1 + } + } + { + longest_match = "" + for (i = 1; i <= module_count; i++) { + module_path = module_paths[i] + if (($1 == module_path || index($1, module_path "/") == 1) && + length(module_path) > length(longest_match)) longest_match = module_path + } + print $0, (longest_match == "" ? "unknown" : attributed_path[longest_match]) + } + ' +} + +build_go_index() { + log "Generating dependency index..." + join_licenses_per_package "${COMBINED_CSV}" | drop_local_module | append_module_path \ + > "${GO_INDEX_FILE}" + + [[ -s "${GO_INDEX_FILE}" ]] \ + || die "go-licenses produced no entries for ${PACKAGE_PATTERNS[*]} — refusing to write empty notices file." + + # go-licenses reports an unclassifiable license as "Unknown" and exits 0. + if cut -d, -f3 "${GO_INDEX_FILE}" | LC_ALL=C grep -qE '^$|(^| / )Unknown( / |$)'; then + die "go-licenses could not classify the license of some packages." \ + "Identify them by hand rather than committing a file that says 'Unknown'." + fi + + if cut -d, -f4 "${GO_INDEX_FILE}" | LC_ALL=C grep -qE '^$|^unknown$'; then + die "could not resolve the module path for some packages from ${MODULES_TXT}." \ + "Run 'make vendor' and re-run, rather than committing a file with unattributed entries." + fi + + local package_path _rest_of_row + while IFS=, read -r package_path _rest_of_row; do + [[ -z "${package_path}" ]] && continue + [[ -n "$(license_files_in "${LICENSES_DIR}/${package_path}")" ]] \ + || die "no license text was saved for ${package_path}." \ + "Re-run after 'make vendor'; do not commit an entry without its license." + done < "${GO_INDEX_FILE}" +} + +spdx_id_for_phrase() { + local phrase_mapping + for phrase_mapping in "${LICENSE_PHRASE_TO_SPDX_ID[@]}"; do + if [[ "$1" == "${phrase_mapping%%|*}" ]]; then + printf '%s' "${phrase_mapping##*|}" + return 0 + fi + done + return 1 +} + +known_phrases_help() { + local phrase_mapping + printf '%s\n' "Phrases this generator recognizes:" + for phrase_mapping in "${LICENSE_PHRASE_TO_SPDX_ID[@]}"; do + printf ' "%s" -> %s\n' "${phrase_mapping%%|*}" "${phrase_mapping##*|}" + done +} + +note_for_path() { + case "$1" in + pkg/pciids/default_pci.ids) + printf '%s' "Upstream offers this database under either the GNU General Public License (version 2 or later) or the 3-clause BSD License. This project distributes it under the 3-clause BSD License, as recorded in the NOTICE file at the root of this repository. The file is embedded into the pkg/pciids package with go:embed, so it is compiled into every consumer of that package. Refresh it with 'make update-pcidb'." + ;; + *) + return 1 + ;; + esac +} + +emit_notice_record() { + local paragraph="$1" bundled_file license_phrase component_name source_url spdx_id note + + if [[ ! "${paragraph}" =~ ${NOTICE_PATH_AND_PHRASE_RE} ]]; then + die "could not derive a bundled file's path and license from ${NOTICE_FILE}:" \ + "" \ + " ${paragraph}" \ + "${NOTICE_FORMAT_HELP[@]}" + fi + bundled_file="${BASH_REMATCH[1]}" + license_phrase="${BASH_REMATCH[2]}" + + if [[ ! "${paragraph}" =~ ${NOTICE_COMPONENT_AND_URL_RE} ]]; then + die "could not derive the component name and source URL for '${bundled_file}' from ${NOTICE_FILE}:" \ + "" \ + " ${paragraph}" \ + "${NOTICE_FORMAT_HELP[@]}" + fi + component_name="${BASH_REMATCH[1]}" + # The URL ends the sentence, so it carries the full stop away with it. + source_url="${BASH_REMATCH[2]%.}" + + if ! spdx_id="$(spdx_id_for_phrase "${license_phrase}")"; then + die "${NOTICE_FILE} elects '${license_phrase}' for '${bundled_file}', which this generator does not know." \ + "Phrases are matched exactly, never guessed." \ + "" \ + "$(known_phrases_help)" \ + "" \ + "Add \"|\" to LICENSE_PHRASE_TO_SPDX_ID, and the verbatim license" \ + "text as ${ASSET_LICENSES_DIR}/.txt." + fi + + [[ -f "${bundled_file}" ]] \ + || die "${NOTICE_FILE} attributes '${bundled_file}', which does not exist." \ + "Point ${NOTICE_FILE} at the file's new location if it moved; do not drop the attribution." + + [[ -s "${ASSET_LICENSES_DIR}/${spdx_id}.txt" ]] \ + || die "${NOTICE_FILE} elects ${spdx_id} for '${bundled_file}', but ${ASSET_LICENSES_DIR}/${spdx_id}.txt does not exist." \ + "An SPDX identifier on its own is not a notice: add the verbatim upstream text there." + + if ! note="$(note_for_path "${bundled_file}")"; then + die "${NOTICE_FILE} attributes '${bundled_file}', which note_for_path() has no note for." \ + "The note records what ${NOTICE_FILE} does not: why there is an election to make," \ + "how the file reaches consumers, and how it is refreshed." + fi + + printf '%s|%s|%s|%s|%s\n' \ + "${bundled_file}" "${component_name}" "${spdx_id}" "${source_url}" "${note}" +} + +build_assets_index() { + log "Reading bundled non-Go file records from ${NOTICE_FILE}..." + + local line paragraph="" + : > "${UNSORTED_ASSETS_INDEX_FILE}" + + # Read as paragraphs: NOTICE wraps its prose, so rewrapping it is not breaking. + while IFS= read -r line || [[ -n "${line}" ]]; do + if [[ -z "${line//[[:space:]]/}" ]]; then + if [[ -n "${paragraph}" ]]; then + emit_notice_record "${paragraph% }" >> "${UNSORTED_ASSETS_INDEX_FILE}" + paragraph="" + fi + continue + fi + paragraph+="${line} " + done < "${NOTICE_FILE}" + if [[ -n "${paragraph}" ]]; then + emit_notice_record "${paragraph% }" >> "${UNSORTED_ASSETS_INDEX_FILE}" + fi + + LC_ALL=C sort -u "${UNSORTED_ASSETS_INDEX_FILE}" > "${ASSETS_INDEX_FILE}" + + [[ -s "${ASSETS_INDEX_FILE}" ]] \ + || die "${NOTICE_FILE} describes no bundled non-Go files." \ + "${NOTICE_FORMAT_HELP[@]}" +} + +# Read from the file, not copied here: 'make update-pcidb' rewrites it wholesale. +extract_leading_comment() { + awk '/^#/ { print; next } { exit }' "$1" +} + +# Filter by name: for restricted licenses 'go-licenses save' copies whole source. +license_files_in() { + local package_dir="$1" candidate_file + [[ -d "${package_dir}" ]] || return 0 + while IFS= read -r -d '' candidate_file; do + if printf '%s' "$(basename "${candidate_file}")" \ + | LC_ALL=C grep -qiE '^(licen[cs]e|notice|copying|copyright|authors|patents)([-._].*)?$'; then + printf '%s\n' "${candidate_file}" + fi + done < <(find "${package_dir}" -maxdepth 1 -type f -print0 2>/dev/null | LC_ALL=C sort -z) +} + +emit_go_index_table() { + local package_path _source_url license_ids module_path + printf '| Package | License | Module |\n' + printf '|---------|---------|--------|\n' + while IFS=, read -r package_path _source_url license_ids module_path; do + [[ -z "${package_path}" ]] && continue + # shellcheck disable=SC2016 # backticks are literal markdown here. + printf '| `%s` | %s | `%s` |\n' "${package_path}" "${license_ids}" "${module_path}" + done < "${GO_INDEX_FILE}" +} + +emit_go_sections() { + local package_path _source_url license_ids module_path license_file + + while IFS=, read -r package_path _source_url license_ids module_path; do + [[ -z "${package_path}" ]] && continue + + printf '### %s\n\n' "${package_path}" + printf '* License: %s\n' "${license_ids}" + printf '* Module: %s\n\n' "${module_path}" + + while IFS= read -r license_file; do + [[ -n "${license_file}" ]] || continue + printf '#### %s\n\n' "$(basename "${license_file}")" + emit_fenced_file "${license_file}" + done < <(license_files_in "${LICENSES_DIR}/${package_path}") + echo + done < "${GO_INDEX_FILE}" +} + +emit_assets_index_table() { + local bundled_file component_name spdx_id source_url note + printf '| File | Component | License | Source |\n' + printf '|------|-----------|---------|--------|\n' + while IFS='|' read -r bundled_file component_name spdx_id source_url note; do + # shellcheck disable=SC2016 # backticks are literal markdown here. + printf '| `%s` | %s | %s | %s |\n' \ + "${bundled_file}" "${component_name}" "${spdx_id}" "${source_url}" + done < "${ASSETS_INDEX_FILE}" +} + +emit_assets_sections() { + local bundled_file component_name spdx_id source_url note + + while IFS='|' read -r bundled_file component_name spdx_id source_url note; do + printf '### %s\n\n' "${component_name}" + # shellcheck disable=SC2016 # backticks are literal markdown here. + printf '* File: `%s`\n' "${bundled_file}" + printf '* License: %s\n' "${spdx_id}" + printf '* Source: %s\n' "${source_url}" + printf '* Note: %s\n\n' "${note}" + + extract_leading_comment "${bundled_file}" > "${ASSET_LEADING_COMMENT_FILE}" + if [[ -s "${ASSET_LEADING_COMMENT_FILE}" ]]; then + printf '#### Notice as it appears in %s\n\n' "${bundled_file}" + emit_fenced_file "${ASSET_LEADING_COMMENT_FILE}" + fi + + printf '#### %s\n\n' "${spdx_id}" + emit_fenced_file "${ASSET_LICENSES_DIR}/${spdx_id}.txt" + echo + done < "${ASSETS_INDEX_FILE}" +} + +compose_document() { + log "Composing ${OUTPUT}..." + { + cat <<'EOF' +# Third-Party Notices + +NVIDIA go-nvlib + +This file lists the third-party dependencies that go-nvlib links into the +packages a consumer imports, along with the verbatim text of each dependency's +license. It covers the **Go modules** in the non-test import closure of the +packages under `pkg/`, and the third-party non-Go files committed into this +repository and compiled into those packages. + +Go standard library packages are excluded; they are covered by the license of +the Go distribution itself. NVIDIA's own code is excluded; it is covered by +`LICENSE`. Dependencies reached only from `_test.go` files are excluded; they +are vendored for testing but a consumer does not link them. + +The `NOTICE` file at the root of this repository remains part of the +distribution. Its entry for `pkg/pciids/default_pci.ids` is reproduced in full +below, so this file stands on its own. + +## Go Dependency Index + +EOF + emit_go_index_table + + cat <<'EOF' + +## Bundled Non-Go File Index + +EOF + emit_assets_index_table + + cat <<'EOF' + +## Go Dependency License Texts + +EOF + emit_go_sections + + cat <<'EOF' +## Bundled Non-Go File Notices + +EOF + emit_assets_sections + } > "${OUTPUT_TMP}" + # mktemp creates 0600, and mv within OUTPUT's directory is an atomic rename. + chmod 644 "${OUTPUT_TMP}" + mv -f "${OUTPUT_TMP}" "${OUTPUT}" +} + +main() { + check_prerequisites + prepare_workspace + + collect_go_dependencies + build_go_index + build_assets_index + compose_document + + local go_package_count bundled_file_count + go_package_count=$(wc -l < "${GO_INDEX_FILE}" | tr -d ' ') + bundled_file_count=$(wc -l < "${ASSETS_INDEX_FILE}" | tr -d ' ') + log "Wrote ${OUTPUT} (${go_package_count} Go packages, ${bundled_file_count} bundled non-Go files)" +} + +main "$@" diff --git a/hack/notices/licenses/BSD-3-Clause.txt b/hack/notices/licenses/BSD-3-Clause.txt new file mode 100644 index 0000000..295908b --- /dev/null +++ b/hack/notices/licenses/BSD-3-Clause.txt @@ -0,0 +1,26 @@ +Copyright (c) Martin Mares and Albert Pool. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/versions.mk b/versions.mk index 2fd96da..8d52ef1 100644 --- a/versions.mk +++ b/versions.mk @@ -37,6 +37,9 @@ CONTROLLER_GEN_VERSION ?= v0.9.2 GOLANGCI_LINT_VERSION ?= v1.52.0 MOQ_VERSION ?= v0.3.4 +# Not image-only like the block above: installed on the host by 'make third-party-notices'. +GO_LICENSES_VERSION ?= v2.0.1 + BUILDIMAGE ?= ghcr.io/nvidia/k8s-test-infra:devel-go$(GOLANG_VERSION) DOCKERFILE_DEVEL := "images/devel/Dockerfile" K8S_TEST_INFRA := "https://github.com/NVIDIA/k8s-test-infra.git"