diff --git a/licenses/library.go b/licenses/library.go index 96f8a8c..9ff05b6 100644 --- a/licenses/library.go +++ b/licenses/library.go @@ -36,7 +36,7 @@ type Library struct { // It may not be the complete set of all packages in the library. Packages []string // Parent go module. - Module *packages.Module + module *Module } // PackagesError aggregates all Packages[].Errors into a single error. @@ -118,7 +118,7 @@ func Libraries(ctx context.Context, classifier Classifier, importPaths ...string for _, p := range pkgs { libraries = append(libraries, &Library{ Packages: []string{p.PkgPath}, - Module: p.Module, + module: newModule(p.Module), }) } continue @@ -128,15 +128,15 @@ func Libraries(ctx context.Context, classifier Classifier, importPaths ...string } for _, pkg := range pkgs { lib.Packages = append(lib.Packages, pkg.PkgPath) - if lib.Module == nil { + if lib.module == nil { // All the sub packages should belong to the same module. - lib.Module = pkg.Module + lib.module = newModule(pkg.Module) } - if lib.Module != nil && lib.Module.Path != "" && lib.Module.Dir == "" { + if lib.module != nil && lib.module.Path != "" && lib.module.Dir == "" { // A known cause is that the module is vendored, so some information is lost. splits := strings.SplitN(lib.LicensePath, "/vendor/", 2) if len(splits) != 2 { - glog.Warningf("module %s does not have dir and it's not vendored, cannot discover the license URL. Report to go-licenses developer if you see this.", lib.Module.Path) + glog.Warningf("module %s does not have dir and it's not vendored, cannot discover the license URL. Report to go-licenses developer if you see this.", lib.module.Path) } else { // This is vendored. Handle this known special case. parentModDir := splits[0] @@ -148,11 +148,11 @@ func Libraries(ctx context.Context, classifier Classifier, importPaths ...string } } if parentPkg == nil { - glog.Warningf("cannot find parent package of vendored module %s", lib.Module.Path) + glog.Warningf("cannot find parent package of vendored module %s", lib.module.Path) } else { // Vendored modules should be commited in the parent module, so it counts as part of the // parent module. - lib.Module = parentPkg.Module + lib.module = newModule(parentPkg.Module) } } } @@ -205,7 +205,7 @@ func (l *Library) FileURL(ctx context.Context, filePath string) (string, error) wrap := func(err error) error { return fmt.Errorf("getting file URL in library %s: %w", l.Name(), err) } - m := l.Module + m := l.module if m == nil { return "", wrap(fmt.Errorf("empty go module info")) } diff --git a/licenses/library_test.go b/licenses/library_test.go index e2938d3..4f86c06 100644 --- a/licenses/library_test.go +++ b/licenses/library_test.go @@ -21,7 +21,6 @@ import ( "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" - "golang.org/x/tools/go/packages" ) func TestLibraries(t *testing.T) { @@ -155,7 +154,7 @@ func TestLibraryFileURL(t *testing.T) { "github.com/google/trillian/crypto", }, LicensePath: "/go/src/github.com/google/trillian/LICENSE", - Module: &packages.Module{ + module: &Module{ Path: "github.com/google/trillian", Dir: "/go/src/github.com/google/trillian", Version: "v1.2.3", @@ -172,7 +171,7 @@ func TestLibraryFileURL(t *testing.T) { "bitbucket.org/user/project/pkg2", }, LicensePath: "/foo/bar/bitbucket.org/user/project/LICENSE", - Module: &packages.Module{ + module: &Module{ Path: "bitbucket.org/user/project", Dir: "/foo/bar/bitbucket.org/user/project", Version: "v1.2.3", @@ -189,7 +188,7 @@ func TestLibraryFileURL(t *testing.T) { "example.com/user/project/pkg2", }, LicensePath: "/foo/bar/example.com/user/project/LICENSE", - Module: &packages.Module{ + module: &Module{ Path: "example.com/user/project", Dir: "/foo/bar/example.com/user/project", Version: "v1.2.3", @@ -206,7 +205,7 @@ func TestLibraryFileURL(t *testing.T) { "github.com/google/trillian/crypto", }, LicensePath: "/go/src/github.com/google/trillian/LICENSE", - Module: &packages.Module{ + module: &Module{ Path: "github.com/google/trillian", Dir: "/go/src/github.com/google/trillian", }, @@ -221,7 +220,7 @@ func TestLibraryFileURL(t *testing.T) { "k8s.io/api/core/v1", }, LicensePath: "/go/modcache/k8s.io/api/LICENSE", - Module: &packages.Module{ + module: &Module{ Path: "k8s.io/api", Dir: "/go/modcache/k8s.io/api", Version: "v0.23.1", diff --git a/licenses/module.go b/licenses/module.go new file mode 100644 index 0000000..eaff1b5 --- /dev/null +++ b/licenses/module.go @@ -0,0 +1,69 @@ +// Copyright 2022 Inc. 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. + +package licenses + +import ( + "strings" + + "golang.org/x/tools/go/packages" +) + +// Module provides module information for a package. +type Module struct { + // Differences from packages.Module: + // * Replace field is removed, it's only an implementation detail in this package. + // If a module is replaced, we'll directly return the replaced module. + // * Version field +incompatible suffix is trimmed. + // * Main, ModuleError, Time, Indirect, GoMod, GoVersion fields are removed, because they are not used. + Path string // module path + Version string // module version + Dir string // directory holding files for this module, if any +} + +func newModule(mod *packages.Module) *Module { + if mod == nil { + return nil + } + // Example of a module with replace directive: k8s.io/kubernetes => k8s.io/kubernetes v1.11.1 + // { + // "Path": "k8s.io/kubernetes", + // "Version": "v0.17.9", + // "Replace": { + // "Path": "k8s.io/kubernetes", + // "Version": "v1.11.1", + // "Time": "2018-07-17T04:20:29Z", + // "Dir": "/home/gongyuan_kubeflow_org/go/pkg/mod/k8s.io/kubernetes@v1.11.1", + // "GoMod": "/home/gongyuan_kubeflow_org/go/pkg/mod/cache/download/k8s.io/kubernetes/@v/v1.11.1.mod" + // }, + // "Dir": "/home/gongyuan_kubeflow_org/go/pkg/mod/k8s.io/kubernetes@v1.11.1", + // "GoMod": "/home/gongyuan_kubeflow_org/go/pkg/mod/cache/download/k8s.io/kubernetes/@v/v1.11.1.mod" + // } + // handle replace directives + // Note, we specifically want to replace version field. + // Haven't confirmed, but we may also need to override the + // entire struct when using replace directive with local folders. + tmp := *mod + if tmp.Replace != nil { + tmp = *tmp.Replace + } + // The +incompatible suffix does not affect module version. + // ref: https://golang.org/ref/mod#incompatible-versions + tmp.Version = strings.TrimSuffix(tmp.Version, "+incompatible") + return &Module{ + Path: tmp.Path, + Version: tmp.Version, + Dir: tmp.Dir, + } +} diff --git a/testdata/modules/replace04/licenses.csv b/testdata/modules/replace04/licenses.csv index a27f538..12a80a8 100644 --- a/testdata/modules/replace04/licenses.csv +++ b/testdata/modules/replace04/licenses.csv @@ -1,2 +1,2 @@ github.com/google/go-licenses/testdata/modules/replace04, https://github.com/google/go-licenses/blob/HEAD/testdata/modules/replace04/LICENSE, Apache-2.0 -github.com/mitchellh/go-homedir, https://github.com/mitchellh/go-homedir/blob/v1.1.0/LICENSE, MIT +github.com/mitchellh/go-homedir, https://github.com/mitchellh/go-homedir/blob/v1.0.0/LICENSE, MIT