2020-05-21 06:30:41 +00:00
|
|
|
/*
|
2020-05-25 16:19:20 +00:00
|
|
|
Copyright 2020 Qiniu Cloud (qiniu.com)
|
2020-05-21 06:30:41 +00:00
|
|
|
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
you may not use this file except in compliance with the License.
|
|
|
|
You may obtain a copy of the License at
|
|
|
|
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
See the License for the specific language governing permissions and
|
|
|
|
limitations under the License.
|
|
|
|
*/
|
|
|
|
|
|
|
|
package build
|
|
|
|
|
|
|
|
import (
|
|
|
|
"crypto/sha256"
|
2020-06-16 05:21:28 +00:00
|
|
|
"errors"
|
2020-05-21 06:30:41 +00:00
|
|
|
"fmt"
|
2020-08-12 02:42:47 +00:00
|
|
|
"io/ioutil"
|
2020-05-21 06:30:41 +00:00
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/qiniu/goc/pkg/cover"
|
2020-06-14 11:38:41 +00:00
|
|
|
log "github.com/sirupsen/logrus"
|
2020-06-15 07:29:49 +00:00
|
|
|
"github.com/spf13/viper"
|
2020-05-21 06:30:41 +00:00
|
|
|
)
|
|
|
|
|
2020-07-26 09:03:47 +00:00
|
|
|
// MvProjectsToTmp moves the projects into a temporary directory
|
2020-06-18 08:20:54 +00:00
|
|
|
func (b *Build) MvProjectsToTmp() error {
|
2020-06-12 09:51:10 +00:00
|
|
|
listArgs := []string{"-json"}
|
|
|
|
if len(b.BuildFlags) != 0 {
|
|
|
|
listArgs = append(listArgs, b.BuildFlags)
|
2020-05-21 06:30:41 +00:00
|
|
|
}
|
|
|
|
listArgs = append(listArgs, "./...")
|
2020-06-18 08:20:54 +00:00
|
|
|
var err error
|
2020-06-18 10:08:48 +00:00
|
|
|
b.Pkgs, err = cover.ListPackages(b.WorkingDir, strings.Join(listArgs, " "), "")
|
2020-06-18 08:20:54 +00:00
|
|
|
if err != nil {
|
|
|
|
log.Errorln(err)
|
|
|
|
return err
|
|
|
|
}
|
2020-05-21 06:30:41 +00:00
|
|
|
|
2020-06-18 08:20:54 +00:00
|
|
|
err = b.mvProjectsToTmp()
|
|
|
|
if err != nil {
|
|
|
|
log.Errorf("Fail to move the project to temporary directory")
|
|
|
|
return err
|
|
|
|
}
|
2020-06-13 10:15:51 +00:00
|
|
|
b.OriGOPATH = os.Getenv("GOPATH")
|
2020-06-12 09:51:10 +00:00
|
|
|
if b.IsMod == true {
|
|
|
|
b.NewGOPATH = ""
|
2020-06-13 10:15:51 +00:00
|
|
|
} else if b.OriGOPATH == "" {
|
2020-06-12 09:51:10 +00:00
|
|
|
b.NewGOPATH = b.TmpDir
|
2020-05-21 06:30:41 +00:00
|
|
|
} else {
|
2020-06-13 10:15:51 +00:00
|
|
|
b.NewGOPATH = fmt.Sprintf("%v:%v", b.TmpDir, b.OriGOPATH)
|
2020-05-21 06:30:41 +00:00
|
|
|
}
|
2020-06-14 01:15:48 +00:00
|
|
|
// fix #14: unable to build project not in GOPATH in legacy mode
|
|
|
|
// this kind of project does not have a pkg.Root value
|
2020-06-16 06:59:01 +00:00
|
|
|
// go 1.11, 1.12 has no pkg.Root,
|
|
|
|
// so add b.IsMod == false as secondary judgement
|
|
|
|
if b.Root == "" && b.IsMod == false {
|
2020-06-14 01:15:48 +00:00
|
|
|
b.NewGOPATH = b.OriGOPATH
|
|
|
|
}
|
2020-06-18 08:20:54 +00:00
|
|
|
log.Infof("New GOPATH: %v", b.NewGOPATH)
|
|
|
|
return nil
|
2020-05-21 06:30:41 +00:00
|
|
|
}
|
|
|
|
|
2020-06-16 05:21:28 +00:00
|
|
|
func (b *Build) mvProjectsToTmp() error {
|
2020-07-26 09:03:47 +00:00
|
|
|
b.TmpDir = filepath.Join(os.TempDir(), tmpFolderName(b.WorkingDir))
|
2020-05-21 06:30:41 +00:00
|
|
|
|
|
|
|
// Delete previous tmp folder and its content
|
2020-06-12 09:51:10 +00:00
|
|
|
os.RemoveAll(b.TmpDir)
|
2020-09-29 12:20:53 +00:00
|
|
|
// Create a new tmp folder and a new importpath for storing cover variables
|
|
|
|
b.GlobalCoverVarImportPath = filepath.Join("src", tmpPackageName(b.WorkingDir))
|
|
|
|
err := os.MkdirAll(filepath.Join(b.TmpDir, b.GlobalCoverVarImportPath), os.ModePerm)
|
2020-05-21 06:30:41 +00:00
|
|
|
if err != nil {
|
2020-08-12 03:43:50 +00:00
|
|
|
return fmt.Errorf("Fail to create the temporary build directory. The err is: %v", err)
|
2020-05-21 06:30:41 +00:00
|
|
|
}
|
2020-08-12 03:43:50 +00:00
|
|
|
log.Infof("Tmp project generated in: %v", b.TmpDir)
|
2020-05-21 06:30:41 +00:00
|
|
|
|
2020-06-16 05:21:28 +00:00
|
|
|
// traverse pkg list to get project meta info
|
|
|
|
b.IsMod, b.Root, err = b.traversePkgsList()
|
2020-06-18 10:08:48 +00:00
|
|
|
log.Infof("mod project? %v", b.IsMod)
|
2020-06-16 05:21:28 +00:00
|
|
|
if errors.Is(err, ErrShouldNotReached) {
|
|
|
|
return fmt.Errorf("mvProjectsToTmp with a empty project: %w", err)
|
|
|
|
}
|
|
|
|
// we should get corresponding working directory in temporary directory
|
|
|
|
b.TmpWorkingDir, err = b.getTmpwd()
|
|
|
|
if err != nil {
|
2020-06-18 08:20:54 +00:00
|
|
|
return fmt.Errorf("getTmpwd failed with error: %w", err)
|
2020-06-16 05:21:28 +00:00
|
|
|
}
|
|
|
|
// issue #14
|
|
|
|
// if b.Root == "", then the project is non-standard project
|
|
|
|
// known cases:
|
|
|
|
// 1. a legacy project, but not in any GOPATH, will cause the b.Root == ""
|
|
|
|
if b.IsMod == false && b.Root != "" {
|
2020-06-12 09:51:10 +00:00
|
|
|
b.cpLegacyProject()
|
2020-06-16 06:59:01 +00:00
|
|
|
} else if b.IsMod == true { // go 1.11, 1.12 has no Build.Root
|
2020-12-14 02:05:59 +00:00
|
|
|
b.cpLegacyProject()
|
2020-08-12 02:42:47 +00:00
|
|
|
updated, newGoModContent, err := b.updateGoModFile()
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("fail to generate new go.mod: %v", err)
|
|
|
|
}
|
|
|
|
if updated {
|
2020-08-12 08:55:32 +00:00
|
|
|
log.Infoln("go.mod needs rewrite")
|
2020-08-12 02:42:47 +00:00
|
|
|
tmpModFile := filepath.Join(b.TmpDir, "go.mod")
|
2020-08-12 03:43:50 +00:00
|
|
|
err := ioutil.WriteFile(tmpModFile, newGoModContent, os.ModePerm)
|
2020-08-12 02:42:47 +00:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("fail to update go.mod: %v", err)
|
|
|
|
}
|
|
|
|
}
|
2020-06-16 05:21:28 +00:00
|
|
|
} else if b.IsMod == false && b.Root == "" {
|
|
|
|
b.TmpWorkingDir = b.TmpDir
|
2020-12-14 02:05:59 +00:00
|
|
|
b.cpLegacyProject()
|
2020-06-16 05:21:28 +00:00
|
|
|
} else {
|
|
|
|
return fmt.Errorf("unknown project type: %w", ErrShouldNotReached)
|
2020-05-21 06:30:41 +00:00
|
|
|
}
|
|
|
|
|
2020-06-16 05:21:28 +00:00
|
|
|
log.Infof("New workingdir in tmp directory in: %v", b.TmpWorkingDir)
|
|
|
|
return nil
|
2020-05-21 06:30:41 +00:00
|
|
|
}
|
|
|
|
|
2020-07-26 09:03:47 +00:00
|
|
|
// tmpFolderName uses the first six characters of the input path's SHA256 checksum
|
|
|
|
// as the suffix.
|
|
|
|
func tmpFolderName(path string) string {
|
2020-05-21 06:30:41 +00:00
|
|
|
sum := sha256.Sum256([]byte(path))
|
|
|
|
h := fmt.Sprintf("%x", sum[:6])
|
|
|
|
|
2020-06-18 08:20:54 +00:00
|
|
|
return "goc-build-" + h
|
2020-05-21 06:30:41 +00:00
|
|
|
}
|
|
|
|
|
2020-09-29 12:20:53 +00:00
|
|
|
// tmpPackageName uses the first six characters of the input path's SHA256 checksum
|
|
|
|
// as the suffix.
|
|
|
|
func tmpPackageName(path string) string {
|
|
|
|
sum := sha256.Sum256([]byte(path))
|
|
|
|
h := fmt.Sprintf("%x", sum[:6])
|
|
|
|
|
|
|
|
return "gocbuild" + h
|
|
|
|
}
|
|
|
|
|
2020-06-16 05:21:28 +00:00
|
|
|
// traversePkgsList travse the Build.Pkgs list
|
|
|
|
// return Build.IsMod, tell if the project is a mod project
|
|
|
|
// return Build.Root:
|
|
|
|
// 1. the project root if it is a mod project,
|
|
|
|
// 2. current GOPATH if it is a legacy project,
|
|
|
|
// 3. some non-standard project, which Build.IsMod == false, Build.Root == nil
|
|
|
|
func (b *Build) traversePkgsList() (isMod bool, root string, err error) {
|
2020-06-12 09:51:10 +00:00
|
|
|
for _, v := range b.Pkgs {
|
2020-06-13 10:15:51 +00:00
|
|
|
// get root
|
2020-06-16 05:21:28 +00:00
|
|
|
root = v.Root
|
2020-05-21 06:30:41 +00:00
|
|
|
if v.Module == nil {
|
2020-06-16 05:21:28 +00:00
|
|
|
return
|
2020-05-21 06:30:41 +00:00
|
|
|
}
|
2020-06-16 05:21:28 +00:00
|
|
|
isMod = true
|
2020-08-11 12:02:48 +00:00
|
|
|
b.ModRoot = v.Module.Dir
|
2020-09-29 12:20:53 +00:00
|
|
|
b.ModRootPath = v.Module.Path
|
2020-06-16 05:21:28 +00:00
|
|
|
return
|
2020-05-21 06:30:41 +00:00
|
|
|
}
|
2020-06-18 08:20:54 +00:00
|
|
|
log.Error(ErrShouldNotReached)
|
2020-06-16 05:21:28 +00:00
|
|
|
err = ErrShouldNotReached
|
|
|
|
return
|
2020-05-21 06:30:41 +00:00
|
|
|
}
|
|
|
|
|
2020-06-12 09:51:10 +00:00
|
|
|
// getTmpwd get the corresponding working directory in the temporary working directory
|
|
|
|
// and store it in the Build.tmpWorkdingDir
|
2020-06-16 05:21:28 +00:00
|
|
|
func (b *Build) getTmpwd() (string, error) {
|
2020-06-12 09:51:10 +00:00
|
|
|
for _, pkg := range b.Pkgs {
|
2020-07-31 07:16:55 +00:00
|
|
|
var index int
|
2020-05-21 06:30:41 +00:00
|
|
|
var parentPath string
|
2020-06-12 09:51:10 +00:00
|
|
|
if b.IsMod == false {
|
2020-06-18 09:03:14 +00:00
|
|
|
index = strings.Index(b.WorkingDir, pkg.Root)
|
2020-05-21 06:30:41 +00:00
|
|
|
parentPath = pkg.Root
|
|
|
|
} else {
|
2020-06-18 09:03:14 +00:00
|
|
|
index = strings.Index(b.WorkingDir, pkg.Module.Dir)
|
2020-05-21 06:30:41 +00:00
|
|
|
parentPath = pkg.Module.Dir
|
|
|
|
}
|
|
|
|
|
|
|
|
if index == -1 {
|
2020-06-16 05:21:28 +00:00
|
|
|
return "", ErrGocShouldExecInProject
|
2020-05-21 06:30:41 +00:00
|
|
|
}
|
2020-06-16 05:21:28 +00:00
|
|
|
// b.TmpWorkingDir = filepath.Join(b.TmpDir, path[len(parentPath):])
|
2020-06-18 09:03:14 +00:00
|
|
|
return filepath.Join(b.TmpDir, b.WorkingDir[len(parentPath):]), nil
|
2020-05-21 06:30:41 +00:00
|
|
|
}
|
|
|
|
|
2020-06-16 05:21:28 +00:00
|
|
|
return "", ErrShouldNotReached
|
2020-05-21 06:30:41 +00:00
|
|
|
}
|
|
|
|
|
2020-06-16 05:21:28 +00:00
|
|
|
func (b *Build) findWhereToInstall() (string, error) {
|
2020-05-21 06:30:41 +00:00
|
|
|
if GOBIN := os.Getenv("GOBIN"); GOBIN != "" {
|
2020-06-16 05:21:28 +00:00
|
|
|
return GOBIN, nil
|
2020-05-21 06:30:41 +00:00
|
|
|
}
|
|
|
|
|
2020-06-12 09:51:10 +00:00
|
|
|
if false == b.IsMod {
|
2020-06-16 05:21:28 +00:00
|
|
|
if b.Root == "" {
|
2020-07-26 09:46:35 +00:00
|
|
|
return "", ErrNoPlaceToInstall
|
2020-05-21 06:30:41 +00:00
|
|
|
}
|
2020-06-16 05:21:28 +00:00
|
|
|
return filepath.Join(b.Root, "bin"), nil
|
2020-05-21 06:30:41 +00:00
|
|
|
}
|
2020-06-18 09:03:14 +00:00
|
|
|
if b.OriGOPATH != "" {
|
|
|
|
return filepath.Join(strings.Split(b.OriGOPATH, ":")[0], "bin"), nil
|
2020-05-21 06:30:41 +00:00
|
|
|
}
|
2020-06-16 05:21:28 +00:00
|
|
|
return filepath.Join(os.Getenv("HOME"), "go", "bin"), nil
|
2020-05-21 06:30:41 +00:00
|
|
|
}
|
2020-06-13 10:15:51 +00:00
|
|
|
|
2020-06-14 04:43:25 +00:00
|
|
|
// Clean clears up the temporary workspace
|
|
|
|
func (b *Build) Clean() error {
|
2020-06-15 07:29:49 +00:00
|
|
|
if !viper.GetBool("debug") {
|
|
|
|
return os.RemoveAll(b.TmpDir)
|
|
|
|
}
|
|
|
|
return nil
|
2020-06-13 10:15:51 +00:00
|
|
|
}
|