blob: dc2810ac160e5caae0c86a23654454df792ccb36 [file] [log] [blame]
# Copyright 2022 Google LLC
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# version 2 as published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# This script uses https://github.com/google/go-licenses to find go dependencies.
import os
import subprocess
from sbom_info_lib import licenses
from sbom_info_lib import license_data
# Max number of processes running in parallel for go-licenses.
PROCESS_LIMIT = 32
GO_LICENSES_TEMPLATE = (
"/mnt/host/source/src/scripts/hooks/install/sbom_info_lib/go-licenses-template"
)
GO_LICENSES_BIN = "/usr/bin/go-licenses"
def shell_find(path, name):
return subprocess.run(
["find", path, "-type", "f", "-name", name],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
def find_go_file(path):
return shell_find(path, "*\\.go").stdout
def get_go_dep(src_path):
# Check if package contains go file.
go_files = find_go_file(src_path)
if not go_files:
return []
res = set()
target_path_list = []
go_files_list = go_files.decode("utf-8").split("\n")
for go_file_path in go_files_list:
if not go_file_path:
continue
# go-licenses can fetch collection of libraries
# used by the package, directly or transitively.
# So only need to run it on main module.
with open(go_file_path, "r") as f:
if "func main()" not in f.read():
continue
target_path_list.append(os.path.dirname(go_file_path))
start_idx = 0
while start_idx < len(target_path_list):
p_list = [
subprocess.Popen(
[
GO_LICENSES_BIN,
"report",
f"{dir_path}/.",
"--template",
GO_LICENSES_TEMPLATE,
],
cwd=dir_path,
stdout=subprocess.PIPE,
stderr=subprocess.DEVNULL,
)
for dir_path in target_path_list[
start_idx : min(start_idx + PROCESS_LIMIT, len(target_path_list))
]
]
start_idx += PROCESS_LIMIT
for p in p_list:
output_bytes, _ = p.communicate()
output = output_bytes.decode("utf-8")
if not output:
continue
for line in output.splitlines():
if not line:
continue
# Output format: [<pkg_name>@<pkg_version>@<license_name>]
parts = line.split("@")
version = parts[1]
license = parts[2]
# Top level package e.g. google-guest-agent and packages in
# vendor/ have version/license "Unknown". They are not needed.
if version == "Unknown" or license == "Unknown":
continue
if license in licenses.LICENSE_MAP:
license = licenses.LICENSE_MAP[license]
parts[2] = license
line = "@".join(parts)
elif license not in license_data.SPDX_LICENSES:
raise licenses.UnknownLicenseError(
f"unknown Go dependency license: {license} in {line}"
)
res.add(line)
return list(res)