forked from mirrors/gomod2nix
1ffea526a0
This makes it possible to generate packages that you do not have a local checkout for, e.g. running: `gomod2nix generate --outdir example/ golang.org/x/tools/cmd/stringer` This will be useful for packaging dependencies that you are not developing, but just simply packaging.
166 lines
3.1 KiB
Go
166 lines
3.1 KiB
Go
package generate
|
|
|
|
import (
|
|
"fmt"
|
|
"go/ast"
|
|
"go/printer"
|
|
"go/token"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"strconv"
|
|
"strings"
|
|
|
|
log "github.com/sirupsen/logrus"
|
|
"golang.org/x/tools/go/vcs"
|
|
)
|
|
|
|
type TempProject struct {
|
|
Dir string
|
|
Install []string
|
|
GoPackagePath string
|
|
}
|
|
|
|
func NewTempProject(packages []string) (*TempProject, error) {
|
|
// Imports without version suffix
|
|
install := make([]string, len(packages))
|
|
for i, imp := range packages {
|
|
idx := strings.Index(imp, "@")
|
|
if idx == -1 {
|
|
idx = len(imp)
|
|
}
|
|
|
|
install[i] = imp[:idx]
|
|
}
|
|
|
|
var goPackagePath string
|
|
|
|
{
|
|
path := install[0]
|
|
|
|
log.WithFields(log.Fields{
|
|
"path": path,
|
|
}).Info("Finding repo root for import path")
|
|
|
|
repoRoot, err := vcs.RepoRootForImportPath(path, false)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
goPackagePath = repoRoot.Root
|
|
}
|
|
|
|
log.Info("Setting up temporary project")
|
|
|
|
dir, err := os.MkdirTemp("", "gomod2nix-proj")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
log.WithFields(log.Fields{
|
|
"dir": dir,
|
|
}).Info("Created temporary directory")
|
|
|
|
// Create tools.go
|
|
{
|
|
log.WithFields(log.Fields{
|
|
"dir": dir,
|
|
}).Info("Creating tools.go")
|
|
|
|
astFile := &ast.File{
|
|
Name: ast.NewIdent("main"),
|
|
Decls: []ast.Decl{
|
|
&ast.GenDecl{
|
|
Tok: token.IMPORT,
|
|
Specs: func() []ast.Spec {
|
|
specs := make([]ast.Spec, len(install))
|
|
|
|
i := 0
|
|
for _, imp := range install {
|
|
specs[i] = &ast.ImportSpec{
|
|
Name: ast.NewIdent("_"),
|
|
Path: &ast.BasicLit{
|
|
ValuePos: token.NoPos,
|
|
Kind: token.STRING,
|
|
Value: strconv.Quote(imp),
|
|
},
|
|
}
|
|
|
|
i++
|
|
}
|
|
|
|
return specs
|
|
}(),
|
|
},
|
|
},
|
|
}
|
|
|
|
f, err := os.Create(filepath.Join(dir, "tools.go"))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Error creating tools.go: %v", err)
|
|
}
|
|
defer f.Close()
|
|
|
|
fset := token.NewFileSet()
|
|
err = printer.Fprint(f, fset, astFile)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error writing tools.go: %v", err)
|
|
}
|
|
|
|
log.WithFields(log.Fields{
|
|
"dir": dir,
|
|
}).Info("Created tools.go")
|
|
}
|
|
|
|
// Set up go module
|
|
{
|
|
log.WithFields(log.Fields{
|
|
"dir": dir,
|
|
}).Info("Initializing go.mod")
|
|
|
|
cmd := exec.Command("go", "mod", "init", "gomod2nix/dummy/package")
|
|
cmd.Dir = dir
|
|
cmd.Stderr = os.Stderr
|
|
|
|
_, err := cmd.Output()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error creating go module: %v", err)
|
|
}
|
|
|
|
log.WithFields(log.Fields{
|
|
"dir": dir,
|
|
}).Info("Done initializing go.mod")
|
|
|
|
// For every dependency fetch it
|
|
for _, imp := range packages {
|
|
log.WithFields(log.Fields{
|
|
"dir": dir,
|
|
"dep": imp,
|
|
}).Info("Getting dependency")
|
|
|
|
cmd := exec.Command("go", "get", "-d", imp)
|
|
cmd.Dir = dir
|
|
cmd.Stderr = os.Stderr
|
|
|
|
_, err := cmd.Output()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error fetching '%s': %v", imp, err)
|
|
}
|
|
|
|
log.WithFields(log.Fields{
|
|
"dir": dir,
|
|
"dep": imp,
|
|
}).Info("Done getting dependency")
|
|
}
|
|
}
|
|
|
|
return &TempProject{
|
|
Dir: dir,
|
|
Install: install,
|
|
GoPackagePath: goPackagePath,
|
|
}, nil
|
|
}
|
|
|
|
func (t *TempProject) Remove() error {
|
|
return os.RemoveAll(t.Dir)
|
|
}
|