Source file src/cmd/go/internal/modconv/convert.go

     1  // Copyright 2018 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package modconv
     6  
     7  import (
     8  	"fmt"
     9  	"os"
    10  	"runtime"
    11  	"sort"
    12  	"strings"
    13  
    14  	"cmd/go/internal/base"
    15  
    16  	"golang.org/x/mod/modfile"
    17  	"golang.org/x/mod/module"
    18  	"golang.org/x/mod/semver"
    19  )
    20  
    21  // ConvertLegacyConfig converts legacy config to modfile.
    22  // The file argument is slash-delimited.
    23  func ConvertLegacyConfig(f *modfile.File, file string, data []byte, queryPackage func(path, rev string) (module.Version, error)) error {
    24  	i := strings.LastIndex(file, "/")
    25  	j := -2
    26  	if i >= 0 {
    27  		j = strings.LastIndex(file[:i], "/")
    28  	}
    29  	convert := Converters[file[i+1:]]
    30  	if convert == nil && j != -2 {
    31  		convert = Converters[file[j+1:]]
    32  	}
    33  	if convert == nil {
    34  		return fmt.Errorf("unknown legacy config file %s", file)
    35  	}
    36  	mf, err := convert(file, data)
    37  	if err != nil {
    38  		return fmt.Errorf("parsing %s: %v", file, err)
    39  	}
    40  
    41  	// Convert requirements block, which may use raw SHA1 hashes as versions,
    42  	// to valid semver requirement list, respecting major versions.
    43  	versions := make([]module.Version, len(mf.Require))
    44  	replace := make(map[string]*modfile.Replace)
    45  
    46  	for _, r := range mf.Replace {
    47  		replace[r.New.Path] = r
    48  		replace[r.Old.Path] = r
    49  	}
    50  
    51  	type token struct{}
    52  	sem := make(chan token, runtime.GOMAXPROCS(0))
    53  	for i, r := range mf.Require {
    54  		m := r.Mod
    55  		if m.Path == "" {
    56  			continue
    57  		}
    58  		if re, ok := replace[m.Path]; ok {
    59  			m = re.New
    60  		}
    61  		sem <- token{}
    62  		go func(i int, m module.Version) {
    63  			defer func() { <-sem }()
    64  			version, err := queryPackage(m.Path, m.Version)
    65  			if err != nil {
    66  				fmt.Fprintf(os.Stderr, "go: converting %s: stat %s@%s: %v\n", base.ShortPath(file), m.Path, m.Version, err)
    67  				return
    68  			}
    69  
    70  			versions[i] = version
    71  		}(i, m)
    72  	}
    73  	// Fill semaphore channel to wait for all tasks to finish.
    74  	for n := cap(sem); n > 0; n-- {
    75  		sem <- token{}
    76  	}
    77  
    78  	need := map[string]string{}
    79  	for _, v := range versions {
    80  		if v.Path == "" {
    81  			continue
    82  		}
    83  		// Don't use semver.Max here; need to preserve +incompatible suffix.
    84  		if needv, ok := need[v.Path]; !ok || semver.Compare(needv, v.Version) < 0 {
    85  			need[v.Path] = v.Version
    86  		}
    87  	}
    88  	paths := make([]string, 0, len(need))
    89  	for path := range need {
    90  		paths = append(paths, path)
    91  	}
    92  	sort.Strings(paths)
    93  	for _, path := range paths {
    94  		if re, ok := replace[path]; ok {
    95  			err := f.AddReplace(re.Old.Path, re.Old.Version, path, need[path])
    96  			if err != nil {
    97  				return fmt.Errorf("add replace: %v", err)
    98  			}
    99  		}
   100  		f.AddNewRequire(path, need[path], false)
   101  	}
   102  
   103  	f.Cleanup()
   104  	return nil
   105  }
   106  

View as plain text