goc/pkg/build/tmpfolder.go

206 lines
5.7 KiB
Go
Raw Normal View History

/*
2020-05-25 16:19:20 +00:00
Copyright 2020 Qiniu Cloud (qiniu.com)
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"
"fmt"
2020-08-12 02:42:47 +00:00
"io/ioutil"
"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-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)
}
listArgs = append(listArgs, "./...")
2020-06-18 08:20:54 +00:00
var err error
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-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
} else {
2020-06-13 10:15:51 +00:00
b.NewGOPATH = fmt.Sprintf("%v:%v", b.TmpDir, b.OriGOPATH)
}
2020-06-18 08:20:54 +00:00
log.Infof("New GOPATH: %v", b.NewGOPATH)
return nil
}
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))
// Delete previous tmp folder and its content
2020-06-12 09:51:10 +00:00
os.RemoveAll(b.TmpDir)
// 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)
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-08-12 03:43:50 +00:00
log.Infof("Tmp project generated in: %v", b.TmpDir)
2020-06-16 05:21:28 +00:00
// traverse pkg list to get project meta info
b.IsMod, b.Root, err = b.traversePkgsList()
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
log.Infof("New workingdir in tmp directory in: %v", b.TmpWorkingDir)
return nil
}
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 {
sum := sha256.Sum256([]byte(path))
h := fmt.Sprintf("%x", sum[:6])
2020-06-18 08:20:54 +00:00
return "goc-build-" + h
}
// 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
if v.Module == nil {
2020-06-16 05:21:28 +00:00
return
}
2020-06-16 05:21:28 +00:00
isMod = true
b.ModRoot = v.Module.Dir
b.ModRootPath = v.Module.Path
2020-06-16 05:21:28 +00:00
return
}
2020-06-18 08:20:54 +00:00
log.Error(ErrShouldNotReached)
2020-06-16 05:21:28 +00:00
err = ErrShouldNotReached
return
}
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
var parentPath string
2020-06-12 09:51:10 +00:00
if b.IsMod == false {
index = strings.Index(b.WorkingDir, pkg.Root)
parentPath = pkg.Root
} else {
index = strings.Index(b.WorkingDir, pkg.Module.Dir)
parentPath = pkg.Module.Dir
}
if index == -1 {
2020-06-16 05:21:28 +00:00
return "", ErrGocShouldExecInProject
}
2020-06-16 05:21:28 +00:00
// b.TmpWorkingDir = filepath.Join(b.TmpDir, path[len(parentPath):])
return filepath.Join(b.TmpDir, b.WorkingDir[len(parentPath):]), nil
}
2020-06-16 05:21:28 +00:00
return "", ErrShouldNotReached
}
2020-06-16 05:21:28 +00:00
func (b *Build) findWhereToInstall() (string, error) {
if GOBIN := os.Getenv("GOBIN"); GOBIN != "" {
2020-06-16 05:21:28 +00:00
return GOBIN, nil
}
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-06-16 05:21:28 +00:00
return filepath.Join(b.Root, "bin"), nil
}
if b.OriGOPATH != "" {
return filepath.Join(strings.Split(b.OriGOPATH, ":")[0], "bin"), nil
}
2020-06-16 05:21:28 +00:00
return filepath.Join(os.Getenv("HOME"), "go", "bin"), nil
}
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
}