gomod2nix/generate/generate.go

219 lines
4.1 KiB
Go
Raw Normal View History

package generate
2020-07-20 10:52:58 +00:00
import (
"bytes"
"crypto/sha256"
"encoding/base64"
2020-07-20 10:52:58 +00:00
"encoding/json"
"fmt"
"io"
2020-07-20 11:29:04 +00:00
"io/ioutil"
"os"
2020-07-20 10:52:58 +00:00
"os/exec"
"path/filepath"
"sort"
"strings"
2022-05-27 17:06:48 +00:00
"sync"
"github.com/nix-community/go-nix/pkg/nar"
log "github.com/sirupsen/logrus"
2022-05-27 17:06:48 +00:00
"github.com/tweag/gomod2nix/lib"
2022-05-27 17:25:35 +00:00
schema "github.com/tweag/gomod2nix/schema"
"golang.org/x/mod/modfile"
2020-07-20 10:52:58 +00:00
)
type goModDownload struct {
Path string
Version string
Info string
GoMod string
Zip string
Dir string
Sum string
GoModSum string
2020-07-20 11:38:32 +00:00
}
func sourceFilter(name string, nodeType nar.NodeType) bool {
return strings.ToLower(filepath.Base(name)) != ".ds_store"
}
func common(directory string) ([]*goModDownload, map[string]string, error) {
goModPath := filepath.Join(directory, "go.mod")
2020-07-20 10:52:58 +00:00
2020-07-21 09:42:32 +00:00
log.WithFields(log.Fields{
"modPath": goModPath,
}).Info("Parsing go.mod")
2020-07-20 11:29:04 +00:00
// Read go.mod
data, err := ioutil.ReadFile(goModPath)
if err != nil {
return nil, nil, err
2020-07-20 11:29:04 +00:00
}
// Parse go.mod
mod, err := modfile.Parse(goModPath, data, nil)
if err != nil {
return nil, nil, err
2020-07-20 11:29:04 +00:00
}
// Map repos -> replacement repo
replace := make(map[string]string)
for _, repl := range mod.Replace {
replace[repl.New.Path] = repl.Old.Path
2020-07-20 11:29:04 +00:00
}
var modDownloads []*goModDownload
{
2022-05-27 17:25:35 +00:00
log.Info("Downloading dependencies")
cmd := exec.Command(
"go", "mod", "download", "--json",
)
cmd.Dir = directory
stdout, err := cmd.Output()
if err != nil {
return nil, nil, err
2020-07-20 11:29:04 +00:00
}
dec := json.NewDecoder(bytes.NewReader(stdout))
for {
var dl *goModDownload
err := dec.Decode(&dl)
if err == io.EOF {
break
2020-07-20 11:29:04 +00:00
}
modDownloads = append(modDownloads, dl)
2020-07-20 11:29:04 +00:00
}
2022-05-27 17:25:35 +00:00
log.Info("Done downloading dependencies")
}
return modDownloads, replace, nil
}
func ImportPkgs(directory string, numWorkers int) error {
modDownloads, _, err := common(directory)
if err != nil {
return err
}
executor := lib.NewParallellExecutor(numWorkers)
for _, dl := range modDownloads {
dl := dl
executor.Add(func() error {
log.WithFields(log.Fields{
"goPackagePath": dl.Path,
}).Info("Importing sources")
pathName := filepath.Base(dl.Path) + "_" + dl.Version
cmd := exec.Command(
"nix-instantiate",
"--eval",
"--expr",
fmt.Sprintf(`
builtins.filterSource (name: type: baseNameOf name != ".DS_Store") (
builtins.path {
path = "%s";
name = "%s";
}
)
`, dl.Dir, pathName),
)
cmd.Stderr = os.Stderr
err = cmd.Start()
if err != nil {
fmt.Println(cmd)
return err
}
err = cmd.Wait()
if err != nil {
fmt.Println(cmd)
return err
}
return nil
})
}
return executor.Wait()
}
func GeneratePkgs(directory string, goMod2NixPath string, numWorkers int) ([]*schema.Package, error) {
modDownloads, replace, err := common(directory)
if err != nil {
return nil, err
}
executor := lib.NewParallellExecutor(numWorkers)
2022-05-27 17:06:48 +00:00
var mux sync.Mutex
cache := schema.ReadCache(goMod2NixPath)
2022-05-27 17:25:35 +00:00
packages := []*schema.Package{}
addPkg := func(pkg *schema.Package) {
mux.Lock()
packages = append(packages, pkg)
mux.Unlock()
}
for _, dl := range modDownloads {
2022-05-27 17:06:48 +00:00
dl := dl
goPackagePath, hasReplace := replace[dl.Path]
if !hasReplace {
goPackagePath = dl.Path
}
2022-05-27 17:06:48 +00:00
cached, ok := cache[goPackagePath]
if ok && cached.Version == dl.Version {
addPkg(cached)
continue
}
2022-05-27 17:06:48 +00:00
executor.Add(func() error {
log.WithFields(log.Fields{
"goPackagePath": goPackagePath,
}).Info("Calculating NAR hash")
h := sha256.New()
err := nar.DumpPathFilter(h, dl.Dir, sourceFilter)
if err != nil {
2022-05-27 17:06:48 +00:00
return err
}
digest := h.Sum(nil)
2020-07-21 09:42:32 +00:00
2022-05-27 17:25:35 +00:00
pkg := &schema.Package{
2022-05-27 17:06:48 +00:00
GoPackagePath: goPackagePath,
Version: dl.Version,
Hash: "sha256-" + base64.StdEncoding.EncodeToString(digest),
2022-05-27 17:06:48 +00:00
}
if hasReplace {
pkg.ReplacedPath = dl.Path
}
addPkg(pkg)
2022-05-27 17:06:48 +00:00
log.WithFields(log.Fields{
"goPackagePath": goPackagePath,
}).Info("Done calculating NAR hash")
2022-05-27 17:06:48 +00:00
return nil
})
}
2022-05-27 17:06:48 +00:00
err = executor.Wait()
if err != nil {
return nil, err
}
sort.Slice(packages, func(i, j int) bool {
return packages[i].GoPackagePath < packages[j].GoPackagePath
})
return packages, nil
2020-07-20 10:52:58 +00:00
}