loader: load packages using Go modules

This commit replaces the existing ad-hoc package loader with a package
loader that uses the x/tools/go/packages package to find all
to-be-loaded packages.
This commit is contained in:
Ayke van Laethem
2020-05-04 23:15:02 +02:00
parent 51daa2a044
commit 25e13d887f
6 changed files with 196 additions and 293 deletions
+55 -6
View File
@@ -10,8 +10,10 @@ import (
"io/ioutil"
"math/rand"
"os"
"os/exec"
"path"
"path/filepath"
"runtime"
"strconv"
"github.com/tinygo-org/tinygo/compileopts"
@@ -58,8 +60,12 @@ func GetCachedGoroot(config *compileopts.Config) (string, error) {
return "", err
}
// Remove the temporary directory if it wasn't moved to the right place
// (for example, when there was an error).
defer os.RemoveAll(tmpgoroot)
for _, name := range []string{"bin", "lib", "pkg"} {
err = os.Symlink(filepath.Join(goroot, name), filepath.Join(tmpgoroot, name))
err = symlink(filepath.Join(goroot, name), filepath.Join(tmpgoroot, name))
if err != nil {
return "", err
}
@@ -72,8 +78,8 @@ func GetCachedGoroot(config *compileopts.Config) (string, error) {
if err != nil {
if os.IsExist(err) {
// Another invocation of TinyGo also seems to have created a GOROOT.
// Use that one instead and delete ours.
os.RemoveAll(tmpgoroot)
// Use that one instead. Our new GOROOT will be automatically
// deleted by the defer above.
return cachedgoroot, nil
}
return "", err
@@ -91,7 +97,7 @@ func mergeDirectory(goroot, tinygoroot, tmpgoroot, importPath string, overrides
// root, so simply make a symlink.
newname := filepath.Join(tmpgoroot, "src", importPath)
oldname := filepath.Join(tinygoroot, "src", importPath)
return os.Symlink(oldname, newname)
return symlink(oldname, newname)
}
// Merge subdirectories. Start by making the directory to merge.
@@ -117,7 +123,7 @@ func mergeDirectory(goroot, tinygoroot, tmpgoroot, importPath string, overrides
// A file, so symlink this.
newname := filepath.Join(tmpgoroot, "src", importPath, e.Name())
oldname := filepath.Join(tinygoroot, "src", importPath, e.Name())
err := os.Symlink(oldname, newname)
err := symlink(oldname, newname)
if err != nil {
return err
}
@@ -143,7 +149,7 @@ func mergeDirectory(goroot, tinygoroot, tmpgoroot, importPath string, overrides
}
newname := filepath.Join(tmpgoroot, "src", importPath, e.Name())
oldname := filepath.Join(goroot, "src", importPath, e.Name())
err := os.Symlink(oldname, newname)
err := symlink(oldname, newname)
if err != nil {
return err
}
@@ -174,3 +180,46 @@ func pathsToOverride(needsSyscallPackage bool) map[string]bool {
}
return paths
}
// symlink creates a symlink or something similar. On Unix-like systems, it
// always creates a symlink. On Windows, it tries to create a symlink and if
// that fails, creates a hardlink or directory junction instead.
//
// Note that while Windows 10 does support symlinks and allows them to be
// created using os.Symlink, it requires developer mode to be enabled.
// Therefore provide a fallback for when symlinking is not possible.
// Unfortunately this fallback only works when TinyGo is installed on the same
// filesystem as the TinyGo cache and the Go installation (which is usually the
// C drive).
func symlink(oldname, newname string) error {
symlinkErr := os.Symlink(oldname, newname)
if runtime.GOOS == "windows" && symlinkErr != nil {
// Fallback for when developer mode is disabled.
// Note that we return the symlink error even if something else fails
// later on. This is because symlinks are the easiest to support
// (they're also used on Linux and MacOS) and enabling them is easy:
// just enable developer mode.
st, err := os.Stat(oldname)
if err != nil {
return symlinkErr
}
if st.IsDir() {
// Make a directory junction. There may be a way to do this
// programmatically, but it involves a lot of magic. Use the mklink
// command built into cmd instead (mklink is a builtin, not an
// external command).
err := exec.Command("cmd", "/k", "mklink", "/J", newname, oldname).Run()
if err != nil {
return symlinkErr
}
} else {
// Make a hard link.
err := os.Link(oldname, newname)
if err != nil {
return symlinkErr
}
}
return nil // success
}
return symlinkErr
}