Skip to content

Commit

Permalink
cmd/go: refactor modload.InitMod
Browse files Browse the repository at this point in the history
InitMod is split into two functions. LoadModFile parses an existing
go.mod file and loads the build list (or checks vendor/modules.txt for
consistency in vendor mode). CreateModFile creates a new go.mod file,
possibly inferring the module path and importing a vendor
configuration file.

Some logic is moved from runInit to CreateModFile. init-specific logic
is removed from other functions.

This CL shouldn't cause substantial differences in behavior, though
some error messages are slightly different.

For #41712

Change-Id: Ia684945cfcf5beca30bbb81e7144fc246c4f27ed
Reviewed-on: https://go-review.googlesource.com/c/go/+/264621
Trust: Jay Conrod <jayconrod@google.com>
Run-TryBot: Jay Conrod <jayconrod@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Bryan C. Mills <bcmills@google.com>
  • Loading branch information
Jay Conrod committed Oct 23, 2020
1 parent c8c3c29 commit d05e89a
Show file tree
Hide file tree
Showing 7 changed files with 73 additions and 72 deletions.
2 changes: 1 addition & 1 deletion src/cmd/go/internal/list/list.go
Expand Up @@ -415,7 +415,7 @@ func runList(ctx context.Context, cmd *base.Command, args []string) {
base.Fatalf("go list -m: not using modules")
}

modload.InitMod(ctx) // Parses go.mod and sets cfg.BuildMod.
modload.LoadModFile(ctx) // Parses go.mod and sets cfg.BuildMod.
if cfg.BuildMod == "vendor" {
const actionDisabledFormat = "go list -m: can't %s using the vendor directory\n\t(Use -mod=mod or -mod=readonly to bypass.)"

Expand Down
2 changes: 1 addition & 1 deletion src/cmd/go/internal/modcmd/download.go
Expand Up @@ -87,7 +87,7 @@ func runDownload(ctx context.Context, cmd *base.Command, args []string) {
if len(args) == 0 {
args = []string{"all"}
} else if modload.HasModRoot() {
modload.InitMod(ctx) // to fill Target
modload.LoadModFile(ctx) // to fill Target
targetAtLatest := modload.Target.Path + "@latest"
targetAtUpgrade := modload.Target.Path + "@upgrade"
targetAtPatch := modload.Target.Path + "@patch"
Expand Down
17 changes: 4 additions & 13 deletions src/cmd/go/internal/modcmd/init.go
Expand Up @@ -10,8 +10,6 @@ import (
"cmd/go/internal/base"
"cmd/go/internal/modload"
"context"
"os"
"strings"
)

var cmdInit = &base.Command{
Expand All @@ -33,21 +31,14 @@ func init() {
}

func runInit(ctx context.Context, cmd *base.Command, args []string) {
modload.CmdModInit = true
if len(args) > 1 {
base.Fatalf("go mod init: too many arguments")
}
var modPath string
if len(args) == 1 {
modload.CmdModModule = args[0]
modPath = args[0]
}

modload.ForceUseModules = true
modFilePath := modload.ModFilePath()
if _, err := os.Stat(modFilePath); err == nil {
base.Fatalf("go mod init: go.mod already exists")
}
if strings.Contains(modload.CmdModModule, "@") {
base.Fatalf("go mod init: module path must not contain '@'")
}
modload.InitMod(ctx) // does all the hard work
modload.WriteGoMod()
modload.CreateModFile(ctx, modPath) // does all the hard work
}
2 changes: 1 addition & 1 deletion src/cmd/go/internal/modload/buildlist.go
Expand Up @@ -37,7 +37,7 @@ var buildList []module.Version
//
// The caller must not modify the returned list.
func LoadAllModules(ctx context.Context) []module.Version {
InitMod(ctx)
LoadModFile(ctx)
ReloadBuildList()
WriteGoMod()
return buildList
Expand Down
116 changes: 63 additions & 53 deletions src/cmd/go/internal/modload/init.go
Expand Up @@ -50,9 +50,6 @@ var (

gopath string

CmdModInit bool // running 'go mod init'
CmdModModule string // module argument for 'go mod init'

// RootMode determines whether a module root is needed.
RootMode Root

Expand Down Expand Up @@ -163,9 +160,9 @@ func Init() {
os.Setenv("GIT_SSH_COMMAND", "ssh -o ControlMaster=no")
}

if CmdModInit {
// Running 'go mod init': go.mod will be created in current directory.
modRoot = base.Cwd
if modRoot != "" {
// modRoot set before Init was called ("go mod init" does this).
// No need to search for go.mod.
} else if RootMode == NoRoot {
if cfg.ModFile != "" && !base.InGOFLAGS("-modfile") {
base.Fatalf("go: -modfile cannot be used with commands that ignore the current module")
Expand Down Expand Up @@ -202,8 +199,7 @@ func Init() {
base.Fatalf("go: -modfile=%s: file does not have .mod extension", cfg.ModFile)
}

// We're in module mode. Install the hooks to make it work.

// We're in module mode. Set any global variables that need to be set.
list := filepath.SplitList(cfg.BuildContext.GOPATH)
if len(list) == 0 || list[0] == "" {
base.Fatalf("missing $GOPATH")
Expand Down Expand Up @@ -270,10 +266,6 @@ func WillBeEnabled() bool {
return false
}

if CmdModInit {
// Running 'go mod init': go.mod will be created in current directory.
return true
}
if modRoot := findModuleRoot(base.Cwd); modRoot == "" {
// GO111MODULE is 'auto', and we can't find a module root.
// Stay in GOPATH mode.
Expand Down Expand Up @@ -347,16 +339,16 @@ func die() {
base.Fatalf("go: cannot find main module; see 'go help modules'")
}

// InitMod sets Target and, if there is a main module, parses the initial build
// list from its go.mod file. If InitMod is called by 'go mod init', InitMod
// will populate go.mod in memory, possibly importing dependencies from a
// legacy configuration file. For other commands, InitMod may make other
// adjustments in memory, like adding a go directive. WriteGoMod should be
// called later to write changes out to disk.
// LoadModFile sets Target and, if there is a main module, parses the initial
// build list from its go.mod file.
//
// LoadModFile may make changes in memory, like adding a go directive and
// ensuring requirements are consistent. WriteGoMod should be called later to
// write changes out to disk or report errors in readonly mode.
//
// As a side-effect, InitMod sets a default for cfg.BuildMod if it does not
// As a side-effect, LoadModFile sets a default for cfg.BuildMod if it does not
// already have an explicit value.
func InitMod(ctx context.Context) {
func LoadModFile(ctx context.Context) {
if len(buildList) > 0 {
return
}
Expand All @@ -369,13 +361,6 @@ func InitMod(ctx context.Context) {
return
}

if CmdModInit {
// Running go mod init: do legacy module conversion
legacyModInit()
modFileToBuildList()
return
}

gomod := ModFilePath()
data, err := lockedfile.Read(gomod)
if err != nil {
Expand Down Expand Up @@ -408,6 +393,50 @@ func InitMod(ctx context.Context) {
}
}

// CreateModFile initializes a new module by creating a go.mod file.
//
// If modPath is empty, CreateModFile will attempt to infer the path from the
// directory location within GOPATH.
//
// If a vendoring configuration file is present, CreateModFile will attempt to
// translate it to go.mod directives. The resulting build list may not be
// exactly the same as in the legacy configuration (for example, we can't get
// packages at multiple versions from the same module).
func CreateModFile(ctx context.Context, modPath string) {
modRoot = base.Cwd
Init()
modFilePath := ModFilePath()
if _, err := os.Stat(modFilePath); err == nil {
base.Fatalf("go: %s already exists", modFilePath)
}

if modPath == "" {
var err error
modPath, err = findModulePath(modRoot)
if err != nil {
base.Fatalf("go: %v", err)
}
} else if err := checkModulePathLax(modPath); err != nil {
base.Fatalf("go: %v", err)
}

fmt.Fprintf(os.Stderr, "go: creating new go.mod: module %s\n", modPath)
modFile = new(modfile.File)
modFile.AddModuleStmt(modPath)
addGoStmt() // Add the go directive before converted module requirements.

convertedFrom, err := convertLegacyConfig(modPath)
if convertedFrom != "" {
fmt.Fprintf(os.Stderr, "go: copying requirements from %s\n", base.ShortPath(convertedFrom))
}
if err != nil {
base.Fatalf("go: %v", err)
}

modFileToBuildList()
WriteGoMod()
}

// checkModulePathLax checks that the path meets some minimum requirements
// to avoid confusing users or the module cache. The requirements are weaker
// than those of module.CheckPath to allow room for weakening module path
Expand Down Expand Up @@ -574,34 +603,23 @@ func setDefaultBuildMod() {
cfg.BuildMod = "readonly"
}

func legacyModInit() {
if modFile == nil {
path, err := findModulePath(modRoot)
if err != nil {
base.Fatalf("go: %v", err)
}
fmt.Fprintf(os.Stderr, "go: creating new go.mod: module %s\n", path)
modFile = new(modfile.File)
modFile.AddModuleStmt(path)
addGoStmt() // Add the go directive before converted module requirements.
}

// convertLegacyConfig imports module requirements from a legacy vendoring
// configuration file, if one is present.
func convertLegacyConfig(modPath string) (from string, err error) {
for _, name := range altConfigs {
cfg := filepath.Join(modRoot, name)
data, err := ioutil.ReadFile(cfg)
if err == nil {
convert := modconv.Converters[name]
if convert == nil {
return
return "", nil
}
fmt.Fprintf(os.Stderr, "go: copying requirements from %s\n", base.ShortPath(cfg))
cfg = filepath.ToSlash(cfg)
if err := modconv.ConvertLegacyConfig(modFile, cfg, data); err != nil {
base.Fatalf("go: %v", err)
}
return
err := modconv.ConvertLegacyConfig(modFile, cfg, data)
return name, err
}
}
return "", nil
}

// addGoStmt adds a go directive to the go.mod file if it does not already include one.
Expand Down Expand Up @@ -681,14 +699,6 @@ func findAltConfig(dir string) (root, name string) {
}

func findModulePath(dir string) (string, error) {
if CmdModModule != "" {
// Running go mod init x/y/z; return x/y/z.
if err := module.CheckImportPath(CmdModModule); err != nil {
return "", err
}
return CmdModModule, nil
}

// TODO(bcmills): once we have located a plausible module path, we should
// query version control (if available) to verify that it matches the major
// version of the most recent tag.
Expand Down
4 changes: 2 additions & 2 deletions src/cmd/go/internal/modload/load.go
Expand Up @@ -169,7 +169,7 @@ type PackageOpts struct {
// LoadPackages identifies the set of packages matching the given patterns and
// loads the packages in the import graph rooted at that set.
func LoadPackages(ctx context.Context, opts PackageOpts, patterns ...string) (matches []*search.Match, loadedPackages []string) {
InitMod(ctx)
LoadModFile(ctx)
if opts.Tags == nil {
opts.Tags = imports.Tags()
}
Expand Down Expand Up @@ -494,7 +494,7 @@ func pathInModuleCache(dir string) string {
// ImportFromFiles adds modules to the build list as needed
// to satisfy the imports in the named Go source files.
func ImportFromFiles(ctx context.Context, gofiles []string) {
InitMod(ctx)
LoadModFile(ctx)

tags := imports.Tags()
imports, testImports, err := imports.ScanFiles(gofiles, tags)
Expand Down
2 changes: 1 addition & 1 deletion src/cmd/go/testdata/script/mod_init_path.txt
@@ -1,7 +1,7 @@
env GO111MODULE=on

! go mod init .
stderr 'malformed import path'
stderr '^go: invalid module path "\.": is a local import path$'

cd x
go mod init example.com/x
Expand Down

0 comments on commit d05e89a

Please sign in to comment.