Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 23 additions & 8 deletions licenses/library.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ package licenses
import (
"context"
"fmt"
"go/build"
"os/exec"
"path/filepath"
"sort"
"strings"
Expand Down Expand Up @@ -83,6 +83,10 @@ func Libraries(ctx context.Context, classifier Classifier, includeTests bool, ig
if err != nil {
return nil, err
}
goRoot, err := activeGoRoot(ctx)
if err != nil {
return nil, err
}

vendoredSearch := []*Module{}
for _, parentPkg := range rootPkgs {
Expand Down Expand Up @@ -120,7 +124,7 @@ func Libraries(ctx context.Context, classifier Classifier, includeTests bool, ig
pkgErrorOccurred = true
return false
}
if isStdLib(p) {
if isStdLib(p, goRoot) {
// No license requirements for the Go standard library.
return false
}
Expand Down Expand Up @@ -409,21 +413,32 @@ func (l *Library) Version() string {
return ""
}

func activeGoRoot(ctx context.Context) (string, error) {
output, err := exec.CommandContext(ctx, "go", "env", "GOROOT").Output()
if err != nil {
return "", fmt.Errorf("resolve active Go toolchain GOROOT: %w", err)
}
goRoot := strings.TrimSpace(string(output))
if goRoot == "" || strings.ContainsAny(goRoot, "\r\n") || !filepath.IsAbs(goRoot) {
return "", fmt.Errorf("active Go toolchain returned invalid GOROOT %q", goRoot)
}
return filepath.Clean(goRoot), nil
}

// isStdLib returns true if this package is part of the Go standard library.
func isStdLib(pkg *packages.Package) bool {
func isStdLib(pkg *packages.Package, goRoot string) bool {
if pkg.Name == "unsafe" {
// Special case unsafe stdlib, because it does not contain go files.
return true
}
if len(pkg.GoFiles) == 0 {
return false
}
prefix := build.Default.GOROOT
sep := string(filepath.Separator)
if !strings.HasSuffix(prefix, sep) {
prefix += sep
relativePath, err := filepath.Rel(filepath.Join(goRoot, "src"), pkg.GoFiles[0])
if err != nil || filepath.IsAbs(relativePath) || relativePath == ".." {
return false
}
return strings.HasPrefix(pkg.GoFiles[0], prefix)
return !strings.HasPrefix(relativePath, ".."+string(filepath.Separator))
}

// isTestBinary returns true iff pkg is a test binary.
Expand Down
29 changes: 29 additions & 0 deletions licenses/library_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,42 @@ package licenses
import (
"context"
"os"
"path/filepath"
"testing"
"time"

"github.com/google/go-cmp/cmp"
"github.com/google/go-licenses/v2/internal/third_party/pkgsite/source"
"golang.org/x/tools/go/packages"
)

func TestIsStdLibUsesActiveToolchainRoot(t *testing.T) {
toolchainsRoot := t.TempDir()
activeRoot := filepath.Join(toolchainsRoot, "go1.26.6")
compiledRoot := filepath.Join(toolchainsRoot, "go1.26.5")
standardPackage := &packages.Package{
Name: "fmt",
GoFiles: []string{filepath.Join(activeRoot, "src", "fmt", "print.go")},
}

if !isStdLib(standardPackage, activeRoot) {
t.Fatal("package from the active toolchain GOROOT was not identified as standard library")
}
if isStdLib(standardPackage, compiledRoot) {
t.Fatal("package was identified using the tool's compile-time GOROOT")
}
prefixCollision := &packages.Package{
Name: "fmt",
GoFiles: []string{filepath.Join(activeRoot+"-other", "src", "fmt", "print.go")},
}
if isStdLib(prefixCollision, activeRoot) {
t.Fatal("GOROOT path prefix collision was identified as standard library")
}
if !isStdLib(&packages.Package{Name: "unsafe"}, activeRoot) {
t.Fatal("unsafe package was not identified as standard library")
}
}

func TestLibraries(t *testing.T) {
wd, err := os.Getwd()
if err != nil {
Expand Down