blob: 9f8924db46788a12558617078820fd153684354b [file] [log] [blame]
// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"flag"
"fmt"
"go/go2go"
"io/ioutil"
"log"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
)
var gotool = filepath.Join(runtime.GOROOT(), "bin", "go")
var cmds = map[string]bool{
"build": true,
"run": true,
"test": true,
"translate": true,
}
// tagsFlag is the implementation of the -tags flag.
type tagsFlag []string
var buildTags tagsFlag
func (v *tagsFlag) Set(s string) error {
// Split on commas, ignore empty strings.
*v = []string{}
for _, s := range strings.Split(s, ",") {
if s != "" {
*v = append(*v, s)
}
}
return nil
}
func (v *tagsFlag) String() string {
return strings.Join(*v, ",")
}
func main() {
flag.Usage = usage
flag.Parse()
args := flag.Args()
if len(args) < 1 {
usage()
}
if !cmds[args[0]] {
usage()
}
cmd := args[0]
fs := flag.NewFlagSet("", flag.ContinueOnError)
fs.Var((*tagsFlag)(&buildTags), "tags", "tag,list")
fs.Parse(args[1:])
args = fs.Args()
importerTmpdir, err := ioutil.TempDir("", "go2go")
if err != nil {
log.Fatal(err)
}
defer os.RemoveAll(importerTmpdir)
importer := go2go.NewImporter(importerTmpdir)
if len(buildTags) > 0 {
importer.SetTags(buildTags)
}
var rundir string
if cmd == "run" {
tmpdir := copyToTmpdir(args)
defer os.RemoveAll(tmpdir)
translate(importer, tmpdir)
var nargs []string
for _, arg := range args {
base := filepath.Base(arg)
f := strings.TrimSuffix(base, ".go2") + ".go"
nargs = append(nargs, f)
}
args = nargs
rundir = tmpdir
} else if cmd == "translate" && isGo2Files(args...) {
for _, arg := range args {
translateFile(importer, arg)
}
} else {
for _, dir := range expandPackages(args) {
translate(importer, dir)
}
}
if cmd != "translate" {
if len(buildTags) > 0 {
args = append([]string{
fmt.Sprintf("-tags=%s", strings.Join(buildTags, ",")),
}, args...)
}
args = append([]string{cmd}, args...)
cmd := exec.Command(gotool, args...)
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Dir = rundir
gopath := importerTmpdir
if go2path := os.Getenv("GO2PATH"); go2path != "" {
gopath += string(os.PathListSeparator) + go2path
}
if oldGopath := os.Getenv("GOPATH"); oldGopath != "" {
gopath += string(os.PathListSeparator) + oldGopath
}
cmd.Env = append(os.Environ(),
"GOPATH="+gopath,
"GO111MODULE=off",
)
if err := cmd.Run(); err != nil {
die(fmt.Sprintf("%s %v failed: %v", gotool, args, err))
}
}
}
// isGo2Files reports whether the arguments are a list of .go2 files.
func isGo2Files(args ...string) bool {
for _, arg := range args {
if filepath.Ext(arg) != ".go2" {
return false
}
}
return true
}
// expandPackages returns a list of directories expanded from packages.
func expandPackages(pkgs []string) []string {
if len(pkgs) == 0 {
return []string{"."}
}
go2path := os.Getenv("GO2PATH")
var dirs []string
pkgloop:
for _, pkg := range pkgs {
if go2path != "" {
for _, pd := range strings.Split(go2path, string(os.PathListSeparator)) {
d := filepath.Join(pd, "src", pkg)
if fi, err := os.Stat(d); err == nil && fi.IsDir() {
dirs = append(dirs, d)
continue pkgloop
}
}
}
cmd := exec.Command(gotool, "list", "-f", "{{.Dir}}", pkg)
cmd.Stderr = os.Stderr
if go2path != "" {
gopath := go2path
if oldGopath := os.Getenv("GOPATH"); oldGopath != "" {
gopath += string(os.PathListSeparator) + oldGopath
}
cmd.Env = append(os.Environ(),
"GOPATH="+gopath,
"GO111MODULE=off",
)
}
out, err := cmd.Output()
if err != nil {
die(fmt.Sprintf("%s list %q failed: %v", gotool, pkg, err))
}
dirs = append(dirs, strings.Split(string(out), "\n")...)
}
return dirs
}
// copyToTmpdir copies files into a temporary directory.
func copyToTmpdir(files []string) string {
if len(files) == 0 {
die("no files to run")
}
tmpdir, err := ioutil.TempDir("", "go2go-run")
if err != nil {
die(err.Error())
}
for _, file := range files {
data, err := ioutil.ReadFile(file)
if err != nil {
die(err.Error())
}
if err := ioutil.WriteFile(filepath.Join(tmpdir, filepath.Base(file)), data, 0444); err != nil {
die(err.Error())
}
}
return tmpdir
}
// usage reports a usage message and exits with failure.
func usage() {
fmt.Fprint(os.Stderr, `Usage: go2go <command> [arguments]
The commands are:
build translate and build packages
run translate and run list of files
test translate and test packages
translate translate .go2 files into .go files
`)
os.Exit(2)
}
// die reports an error and exits.
func die(msg string) {
fmt.Fprintln(os.Stderr, msg)
os.Exit(1)
}