gomod2nix/internal/generate/temp.go
2023-11-14 09:17:40 +01:00

190 lines
3.7 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/mod/module"
"golang.org/x/tools/go/vcs" // nolint:staticcheck
)
type TempProject struct {
Dir string
SubPackages []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
for _, path := range install {
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
}
_, versionSuffix, _ := module.SplitPathVersion(path)
p := repoRoot.Root + versionSuffix
if goPackagePath != "" && p != goPackagePath {
return nil, fmt.Errorf("Mixed origin packages are not allowed")
}
goPackagePath = p
}
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
{
log.WithFields(log.Fields{
"dir": dir,
}).Info("Getting dependencies")
args := []string{"get", "-d"}
args = append(args, packages...)
cmd := exec.Command("go", args...)
cmd.Dir = dir
cmd.Stderr = os.Stderr
_, err := cmd.Output()
if err != nil {
return nil, fmt.Errorf("error fetching: %v", err)
}
log.WithFields(log.Fields{
"dir": dir,
}).Info("Done getting dependencies")
}
}
subPackages := []string{}
{
prefix, versionSuffix, _ := module.SplitPathVersion(goPackagePath)
for _, path := range install {
p := strings.TrimPrefix(path, prefix)
p = strings.TrimSuffix(p, versionSuffix)
p = strings.TrimPrefix(p, "/")
if p == "" {
continue
}
subPackages = append(subPackages, p)
}
}
return &TempProject{
Dir: dir,
SubPackages: subPackages,
GoPackagePath: goPackagePath,
}, nil
}
func (t *TempProject) Remove() error {
return os.RemoveAll(t.Dir)
}