Skip to content
This repository has been archived by the owner on Nov 18, 2021. It is now read-only.

Commit

Permalink
cmd/cue: embed packages required for get go checking
Browse files Browse the repository at this point in the history
'cue get go' loads interface types from cmd/cue/cmd/interfaces during
initialisation. Go types that satisfy these interface types are then
used as special cases for the target CUE definition types - see the
complete logic in (*extractor).altType.

Given the nature of 'cue get go', it is assumed that the command is run
in the context of a Go module. That is to say, 'go env GOMOD' will be
non-empty. (It doesn't seem worth adding GOPATH support given that
modules will be GA in Go 1.16)

Currently, 'cue get go' assumes that the package
cuelang.org/go/cmd/cue/cmd/interfaces will be available. That is to say,
either cuelang.org/go is the main module or, more likely, that
cuelang.org/go is a module dependency of the main module. However, this
is an unnecessary restriction and one that is potentially incorrect.
After all, the user is using a specific version of cmd/cue - loading a
different version of cuelang.org/go seems likely to lead to version-skew
related problems.

This change switches cmd/cue to instead load
cuelang.org/go/cmd/cue/cmd/interfaces via a simple embedding of the
GoFiles in that package.

As part of the same change we surface all loading and type checking
errors for either the embedded cuelang.org/go/cmd/cue/cmd/interfaces or
the arguments to 'cue get go'.

With thanks to @verdverm and @NicolasRouquette for the analysis and
suggestions that went into this fix.

Drops GOPATH support.

Fixes #621
Fixes #668

Change-Id: I630e2cb167c05d63186aa356eaf70a58749b61c5
  • Loading branch information
myitcv committed Jan 29, 2021
1 parent 148277e commit 5b770cd
Show file tree
Hide file tree
Showing 20 changed files with 827 additions and 571 deletions.
94 changes: 87 additions & 7 deletions cmd/cue/cmd/get_go.go
Original file line number Diff line number Diff line change
Expand Up @@ -281,13 +281,59 @@ func (e *extractor) usedPkg(pkg string) {
e.usedPkgs[pkg] = true
}

func initInterfaces() error {
const cueGoMod = `
module cuelang.org/go
`

//go:generate go run cuelang.org/go/internal/cmd/embedpkg cuelang.org/go/cmd/cue/cmd/interfaces

func initInterfaces() (err error) {
// tempdir needed for overlay
tmpDir, err := ioutil.TempDir("", "cuelang")
if err != nil {
return err
}

defer func() {
rerr := os.RemoveAll(tmpDir)
if err == nil {
err = rerr
}
}()

// write the cuelang go.mod
err = ioutil.WriteFile(filepath.Join(tmpDir, "go.mod"), []byte(cueGoMod), 0666)
if err != nil {
return err
}

for fn, contents := range interfacesFiles {
fn = filepath.Join(tmpDir, filepath.FromSlash(fn))
dir := filepath.Dir(fn)
if err := os.MkdirAll(dir, 0777); err != nil {
return err
}

if err = ioutil.WriteFile(fn, contents, 0666); err != nil {
return err
}
}

cfg := &packages.Config{
Mode: packages.LoadAllSyntax,
Mode: packages.NeedName | packages.NeedFiles | packages.NeedCompiledGoFiles | packages.NeedImports | packages.NeedTypes | packages.NeedTypesSizes | packages.NeedSyntax | packages.NeedTypesInfo | packages.NeedDeps,
Dir: filepath.Join(tmpDir),
}

p, err := packages.Load(cfg, "cuelang.org/go/cmd/cue/cmd/interfaces")
if err != nil {
return err
return fmt.Errorf("error loading embedded cuelang.org/go/cmd/cue/cmd/interfaces package: %w", err)
}
if len(p[0].Errors) > 0 {
var buf bytes.Buffer
for _, e := range p[0].Errors {
fmt.Fprintf(&buf, "\t%v\n", e)
}
return fmt.Errorf("error loading embedded cuelang.org/go/cmd/cue/cmd/interfaces package:\n%s", buf.String())
}

for e, tt := range p[0].TypesInfo.Types {
Expand Down Expand Up @@ -321,6 +367,31 @@ var (
// - consider not including types with any dropped fields.

func extract(cmd *Command, args []string) error {
// TODO: there are various permutations when it comes to the arguments
// to get go and the calling context. All require that the
//
// 1. the arguments to
// TODO the CUE load using "." (below) assumes that a CUE module and a Go module will
// exist within the same directory (more precisely that a Go module could be
// nested within a CUE module), such that the module path in any
// subdirectory below the current directory will be the same. This seems an
// entirely reasonable restriction, but also one that we should enforce.
//
// Enforcing this restriction also makes --local entirely redundant.
//
// TODO: GOPATH. Strictly speaking it would be better from a UX perspective
// to eargerly check that the two modules align. But because we still offer
// GOPATH support we can't know the Go module
// It would generally be
// Strictly speaking we could lazily check whether the two modules "align"
// but it seems better and more consistent to check early, by using the
// CUE and Go equivalents of loading ".", then comparing the path result.
// Note we need to do
//
// --local is required for GOPATH mode.

// command specifies a Go package(s) that belong to the main module
// and where for some reason the
// determine module root:
binst := loadFromArgs(cmd, []string{"."}, nil)[0]

Expand All @@ -337,12 +408,21 @@ func extract(cmd *Command, args []string) error {
}

cfg := &packages.Config{
Mode: packages.LoadAllSyntax | packages.NeedModule,
Mode: packages.NeedName | packages.NeedFiles | packages.NeedCompiledGoFiles | packages.NeedImports | packages.NeedTypes | packages.NeedTypesSizes | packages.NeedSyntax | packages.NeedTypesInfo | packages.NeedDeps | packages.NeedModule,
}
pkgs, err := packages.Load(cfg, args...)
if err != nil {
return err
}
var errs []string
for _, P := range pkgs {
if len(P.Errors) > 0 {
errs = append(errs, fmt.Sprintf("\t%s: %v", P.PkgPath, P.Errors))
}
}
if len(errs) > 0 {
return fmt.Errorf("could not load Go packages:\n%s", strings.Join(errs, "\n"))
}

e := extractor{
cmd: cmd,
Expand Down Expand Up @@ -408,7 +488,7 @@ func (e *extractor) extractPkg(root string, p *packages.Package) error {
}
}

if err := os.MkdirAll(dir, 0755); err != nil {
if err := os.MkdirAll(dir, 0777); err != nil {
return err
}

Expand Down Expand Up @@ -481,7 +561,7 @@ func (e *extractor) extractPkg(root string, p *packages.Package) error {
if err != nil {
return err
}
err = ioutil.WriteFile(filepath.Join(dir, file), b, 0644)
err = ioutil.WriteFile(filepath.Join(dir, file), b, 0666)
if err != nil {
return err
}
Expand Down Expand Up @@ -539,7 +619,7 @@ func (e *extractor) importCUEFiles(p *packages.Package, dir, args string) error
w.Write(b)

dst := filepath.Join(dir, file)
if err := ioutil.WriteFile(dst, w.Bytes(), 0644); err != nil {
if err := ioutil.WriteFile(dst, w.Bytes(), 0666); err != nil {
return err
}
}
Expand Down
102 changes: 0 additions & 102 deletions cmd/cue/cmd/get_go_test.go

This file was deleted.

10 changes: 10 additions & 0 deletions cmd/cue/cmd/interfaces_gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 0 additions & 19 deletions cmd/cue/cmd/testdata/code/go/pkg1/alias.go

This file was deleted.

Loading

0 comments on commit 5b770cd

Please sign in to comment.