Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit aa5e14f

Browse files
committed
Go: Replace BuildInfo with GoWorkspace
1 parent 8b376e7 commit aa5e14f

4 files changed

Lines changed: 132 additions & 112 deletions

File tree

go/extractor/autobuilder/build-environment.go

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -267,15 +267,25 @@ func outputEnvironmentJson(version string) {
267267
// Get the version of Go to install and output it to stdout as json.
268268
func IdentifyEnvironment() {
269269
var v versionInfo
270-
buildInfos := project.GetBuildInfo(false)
271-
goVersionInfo := project.TryReadGoDirective(buildInfos[0]) // TODO: find greatest version
272-
v.goModVersion, v.goModVersionFound = goVersionInfo.Version, goVersionInfo.Found
270+
workspaces := project.GetWorkspaceInfo(false)
273271

272+
// Find the greatest Go version required by any of the workspaces.
273+
greatestGoVersion := project.GoVersionInfo{Version: "", Found: false}
274+
for _, workspace := range workspaces {
275+
goVersionInfo := workspace.RequiredGoVersion()
276+
if goVersionInfo.Found && (!greatestGoVersion.Found || semver.Compare("v"+goVersionInfo.Version, "v"+greatestGoVersion.Version) > 0) {
277+
greatestGoVersion = goVersionInfo
278+
}
279+
}
280+
v.goModVersion, v.goModVersionFound = greatestGoVersion.Version, greatestGoVersion.Found
281+
282+
// Find which, if any, version of Go is installed on the system already.
274283
v.goEnvVersionFound = toolchain.IsInstalled()
275284
if v.goEnvVersionFound {
276285
v.goEnvVersion = toolchain.GetEnvGoVersion()[2:]
277286
}
278287

288+
// Determine which version of Go we should recommend to install.
279289
msg, versionToInstall := getVersionToInstall(v)
280290
log.Println(msg)
281291

go/extractor/cli/go-autobuilder/go-autobuilder.go

Lines changed: 73 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -160,16 +160,16 @@ func getSourceDir() string {
160160
}
161161

162162
// fixGoVendorIssues fixes issues with go vendor for go version >= 1.14
163-
func fixGoVendorIssues(buildInfo *project.BuildInfo, goModVersionFound bool) {
164-
if buildInfo.ModMode == project.ModVendor {
163+
func fixGoVendorIssues(workspace *project.GoWorkspace, goModVersionFound bool) {
164+
if workspace.ModMode == project.ModVendor {
165165
// fix go vendor issues with go versions >= 1.14 when no go version is specified in the go.mod
166166
// if this is the case, and dependencies were vendored with an old go version (and therefore
167167
// do not contain a '## explicit' annotation, the go command will fail and refuse to do any
168168
// work
169169
//
170170
// we work around this by adding an explicit go version of 1.13, which is the last version
171171
// where this is not an issue
172-
if buildInfo.DepMode == project.GoGetWithModules {
172+
if workspace.DepMode == project.GoGetWithModules {
173173
if !goModVersionFound {
174174
// if the go.mod does not contain a version line
175175
modulesTxt, err := os.ReadFile("vendor/modules.txt")
@@ -180,7 +180,7 @@ func fixGoVendorIssues(buildInfo *project.BuildInfo, goModVersionFound bool) {
180180
log.Println("Adding a version directive to the go.mod file as the modules.txt does not have explicit annotations")
181181
if !addVersionToMod("1.13") {
182182
log.Println("Failed to add a version to the go.mod file to fix explicitly required package bug; not using vendored dependencies")
183-
buildInfo.ModMode = project.ModMod
183+
workspace.ModMode = project.ModMod
184184
}
185185
}
186186
}
@@ -189,9 +189,9 @@ func fixGoVendorIssues(buildInfo *project.BuildInfo, goModVersionFound bool) {
189189
}
190190

191191
// Determines whether the project needs a GOPATH set up
192-
func getNeedGopath(buildInfo project.BuildInfo, importpath string) bool {
192+
func getNeedGopath(workspace project.GoWorkspace, importpath string) bool {
193193
needGopath := true
194-
if buildInfo.DepMode == project.GoGetWithModules {
194+
if workspace.DepMode == project.GoGetWithModules {
195195
needGopath = false
196196
}
197197
// if `LGTM_INDEX_NEED_GOPATH` is set, it overrides the value for `needGopath` inferred above
@@ -212,43 +212,46 @@ func getNeedGopath(buildInfo project.BuildInfo, importpath string) bool {
212212
}
213213

214214
// Try to update `go.mod` and `go.sum` if the go version is >= 1.16.
215-
func tryUpdateGoModAndGoSum(buildInfo project.BuildInfo) {
215+
func tryUpdateGoModAndGoSum(workspace project.GoWorkspace) {
216216
// Go 1.16 and later won't automatically attempt to update go.mod / go.sum during package loading, so try to update them here:
217-
if buildInfo.ModMode != project.ModVendor && buildInfo.DepMode == project.GoGetWithModules && semver.Compare(toolchain.GetEnvGoSemVer(), "v1.16") >= 0 {
218-
// stat go.mod and go.sum
219-
goModPath := filepath.Join(buildInfo.BaseDir, "go.mod")
220-
beforeGoModFileInfo, beforeGoModErr := os.Stat(goModPath)
221-
if beforeGoModErr != nil {
222-
log.Println("Failed to stat go.mod before running `go mod tidy -e`")
223-
}
217+
if workspace.ModMode != project.ModVendor && workspace.DepMode == project.GoGetWithModules && semver.Compare(toolchain.GetEnvGoSemVer(), "v1.16") >= 0 {
218+
for _, goMod := range workspace.Modules {
219+
// stat go.mod and go.sum
220+
goModPath := goMod.Path
221+
goModDir := filepath.Dir(goModPath)
222+
beforeGoModFileInfo, beforeGoModErr := os.Stat(goModPath)
223+
if beforeGoModErr != nil {
224+
log.Printf("Failed to stat %s before running `go mod tidy -e`\n", goModPath)
225+
}
224226

225-
goSumPath := filepath.Join(buildInfo.BaseDir, "go.sum")
226-
beforeGoSumFileInfo, beforeGoSumErr := os.Stat(goSumPath)
227+
goSumPath := filepath.Join(goModDir, "go.sum")
228+
beforeGoSumFileInfo, beforeGoSumErr := os.Stat(goSumPath)
227229

228-
// run `go mod tidy -e`
229-
cmd := toolchain.TidyModule(buildInfo.BaseDir)
230-
res := util.RunCmd(cmd)
230+
// run `go mod tidy -e`
231+
cmd := toolchain.TidyModule(goModDir)
232+
res := util.RunCmd(cmd)
231233

232-
if !res {
233-
log.Println("Failed to run `go mod tidy -e`")
234-
} else {
235-
if beforeGoModFileInfo != nil {
236-
afterGoModFileInfo, afterGoModErr := os.Stat(goModPath)
237-
if afterGoModErr != nil {
238-
log.Println("Failed to stat go.mod after running `go mod tidy -e`")
239-
} else if afterGoModFileInfo.ModTime().After(beforeGoModFileInfo.ModTime()) {
240-
// if go.mod has been changed then notify the user
241-
log.Println("We have run `go mod tidy -e` and it altered go.mod. You may wish to check these changes into version control. ")
234+
if !res {
235+
log.Printf("Failed to run `go mod tidy -e` in %s\n", goModDir)
236+
} else {
237+
if beforeGoModFileInfo != nil {
238+
afterGoModFileInfo, afterGoModErr := os.Stat(goModPath)
239+
if afterGoModErr != nil {
240+
log.Printf("Failed to stat %s after running `go mod tidy -e`: %s\n", goModPath, afterGoModErr.Error())
241+
} else if afterGoModFileInfo.ModTime().After(beforeGoModFileInfo.ModTime()) {
242+
// if go.mod has been changed then notify the user
243+
log.Println("We have run `go mod tidy -e` and it altered go.mod. You may wish to check these changes into version control. ")
244+
}
242245
}
243-
}
244246

245-
afterGoSumFileInfo, afterGoSumErr := os.Stat(goSumPath)
246-
if afterGoSumErr != nil {
247-
log.Println("Failed to stat go.sum after running `go mod tidy -e`")
248-
} else {
249-
if beforeGoSumErr != nil || afterGoSumFileInfo.ModTime().After(beforeGoSumFileInfo.ModTime()) {
250-
// if go.sum has been changed then notify the user
251-
log.Println("We have run `go mod tidy -e` and it altered go.sum. You may wish to check these changes into version control. ")
247+
afterGoSumFileInfo, afterGoSumErr := os.Stat(goSumPath)
248+
if afterGoSumErr != nil {
249+
log.Printf("Failed to stat %s after running `go mod tidy -e`: %s\n", goSumPath, afterGoSumErr.Error())
250+
} else {
251+
if beforeGoSumErr != nil || afterGoSumFileInfo.ModTime().After(beforeGoSumFileInfo.ModTime()) {
252+
// if go.sum has been changed then notify the user
253+
log.Println("We have run `go mod tidy -e` and it altered go.sum. You may wish to check these changes into version control. ")
254+
}
252255
}
253256
}
254257
}
@@ -431,10 +434,10 @@ func buildWithCustomCommands(inst string) {
431434
}
432435

433436
// Install dependencies using the given dependency installer mode.
434-
func installDependencies(buildInfo project.BuildInfo) {
437+
func installDependencies(workspace project.GoWorkspace) {
435438
// automatically determine command to install dependencies
436439
var install *exec.Cmd
437-
if buildInfo.DepMode == project.Dep {
440+
if workspace.DepMode == project.Dep {
438441
// set up the dep cache if SEMMLE_CACHE is set
439442
cacheDir := os.Getenv("SEMMLE_CACHE")
440443
if cacheDir != "" {
@@ -464,34 +467,41 @@ func installDependencies(buildInfo project.BuildInfo) {
464467
install = exec.Command("dep", "ensure", "-v")
465468
}
466469
log.Println("Installing dependencies using `dep ensure`.")
467-
} else if buildInfo.DepMode == project.Glide {
470+
util.RunCmd(install)
471+
} else if workspace.DepMode == project.Glide {
468472
install = exec.Command("glide", "install")
469473
log.Println("Installing dependencies using `glide install`")
474+
util.RunCmd(install)
470475
} else {
471-
// get dependencies
472-
install = exec.Command("go", "get", "-v", "./...")
473-
install.Dir = buildInfo.BaseDir
474-
log.Printf("Installing dependencies using `go get -v ./...` in `%s`.\n", buildInfo.BaseDir)
476+
// get dependencies for all modules
477+
for _, module := range workspace.Modules {
478+
install = exec.Command("go", "get", "-v", "./...")
479+
install.Dir = filepath.Dir(module.Path)
480+
log.Printf("Installing dependencies using `go get -v ./...` in `%s`.\n", filepath.Dir(module.Path))
481+
util.RunCmd(install)
482+
}
475483
}
476-
util.RunCmd(install)
477484
}
478485

479486
// Run the extractor.
480-
func extract(buildInfo project.BuildInfo) {
487+
func extract(workspace project.GoWorkspace) {
481488
extractor, err := util.GetExtractorPath()
482489
if err != nil {
483490
log.Fatalf("Could not determine path of extractor: %v.\n", err)
484491
}
485492

486493
extractorArgs := []string{}
487-
if buildInfo.DepMode == project.GoGetWithModules {
488-
extractorArgs = append(extractorArgs, buildInfo.ModMode.ArgsForGoVersion(toolchain.GetEnvGoSemVer())...)
494+
if workspace.DepMode == project.GoGetWithModules {
495+
extractorArgs = append(extractorArgs, workspace.ModMode.ArgsForGoVersion(toolchain.GetEnvGoSemVer())...)
496+
}
497+
for _, module := range workspace.Modules {
498+
relModPath, _ := filepath.Rel(workspace.BaseDir, filepath.Dir(module.Path))
499+
extractorArgs = append(extractorArgs, relModPath)
489500
}
490-
extractorArgs = append(extractorArgs, "./...")
491501

492-
log.Printf("Running extractor command '%s %v' from directory '%s'.\n", extractor, extractorArgs, buildInfo.BaseDir)
502+
log.Printf("Running extractor command '%s %v' from directory '%s'.\n", extractor, extractorArgs, workspace.BaseDir)
493503
cmd := exec.Command(extractor, extractorArgs...)
494-
cmd.Dir = buildInfo.BaseDir
504+
cmd.Dir = workspace.BaseDir
495505
cmd.Stdout = os.Stdout
496506
cmd.Stderr = os.Stderr
497507
err = cmd.Run()
@@ -511,13 +521,13 @@ func installDependenciesAndBuild() {
511521

512522
// determine how to install dependencies and whether a GOPATH needs to be set up before
513523
// extraction
514-
buildInfos := project.GetBuildInfo(true)
524+
workspaces := project.GetWorkspaceInfo(true)
515525
if _, present := os.LookupEnv("GO111MODULE"); !present {
516526
os.Setenv("GO111MODULE", "auto")
517527
}
518528

519-
for _, buildInfo := range buildInfos {
520-
goVersionInfo := project.TryReadGoDirective(buildInfo)
529+
for _, workspace := range workspaces {
530+
goVersionInfo := workspace.RequiredGoVersion()
521531

522532
// This diagnostic is not required if the system Go version is 1.21 or greater, since the
523533
// Go tooling should install required Go versions as needed.
@@ -531,12 +541,12 @@ func installDependenciesAndBuild() {
531541
}
532542
}
533543

534-
fixGoVendorIssues(&buildInfo, goVersionInfo.Found)
544+
fixGoVendorIssues(&workspace, goVersionInfo.Found)
535545

536-
tryUpdateGoModAndGoSum(buildInfo)
546+
tryUpdateGoModAndGoSum(workspace)
537547

538548
importpath := getImportPath()
539-
needGopath := getNeedGopath(buildInfo, importpath)
549+
needGopath := getNeedGopath(workspace, importpath)
540550

541551
inLGTM := os.Getenv("LGTM_SRC") != "" || os.Getenv("LGTM_INDEX_NEED_GOPATH") != ""
542552

@@ -557,30 +567,30 @@ func installDependenciesAndBuild() {
557567
inst := util.Getenv("CODEQL_EXTRACTOR_GO_BUILD_COMMAND", "LGTM_INDEX_BUILD_COMMAND")
558568
shouldInstallDependencies := false
559569
if inst == "" {
560-
shouldInstallDependencies = buildWithoutCustomCommands(buildInfo.ModMode)
570+
shouldInstallDependencies = buildWithoutCustomCommands(workspace.ModMode)
561571
} else {
562572
buildWithCustomCommands(inst)
563573
}
564574

565-
if buildInfo.ModMode == project.ModVendor {
575+
if workspace.ModMode == project.ModVendor {
566576
// test if running `go` with -mod=vendor works, and if it doesn't, try to fallback to -mod=mod
567577
// or not set if the go version < 1.14. Note we check this post-build in case the build brings
568578
// the vendor directory up to date.
569579
if !checkVendor() {
570-
buildInfo.ModMode = project.ModMod
580+
workspace.ModMode = project.ModMod
571581
log.Println("The vendor directory is not consistent with the go.mod; not using vendored dependencies.")
572582
}
573583
}
574584

575585
if shouldInstallDependencies {
576-
if buildInfo.ModMode == project.ModVendor {
586+
if workspace.ModMode == project.ModVendor {
577587
log.Printf("Skipping dependency installation because a Go vendor directory was found.")
578588
} else {
579-
installDependencies(buildInfo)
589+
installDependencies(workspace)
580590
}
581591
}
582592

583-
extract(buildInfo)
593+
extract(workspace)
584594
}
585595
}
586596

go/extractor/project/project.go

Lines changed: 46 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,39 @@ type GoVersionInfo struct {
5454
Found bool
5555
}
5656

57+
// Determines the version of Go that is required by this workspace. This is, in order of preference:
58+
// 1. The Go version specified in the `go.work` file, if any.
59+
// 2. The greatest Go version specified in any `go.mod` file, if any.
60+
func (workspace *GoWorkspace) RequiredGoVersion() GoVersionInfo {
61+
if workspace.WorkspaceFile != nil {
62+
// If we have parsed a `go.work` file, return the version number from it.
63+
return GoVersionInfo{Version: workspace.WorkspaceFile.Go.Version, Found: true}
64+
} else if workspace.Modules != nil && len(workspace.Modules) > 0 {
65+
// Otherwise, if we have `go.work` files, find the greatest Go version in those.
66+
var greatestVersion string = ""
67+
for _, module := range workspace.Modules {
68+
if module.Module != nil {
69+
// If we have parsed the file, retrieve the version number we have already obtained.
70+
if greatestVersion == "" || semver.Compare("v"+module.Module.Go.Version, "v"+greatestVersion) > 0 {
71+
greatestVersion = module.Module.Go.Version
72+
}
73+
} else {
74+
modVersion := tryReadGoDirective(module.Path)
75+
if modVersion.Found && (greatestVersion == "" || semver.Compare("v"+modVersion.Version, "v"+greatestVersion) > 0) {
76+
greatestVersion = modVersion.Version
77+
}
78+
}
79+
}
80+
81+
// If we have found some version, return it.
82+
if greatestVersion != "" {
83+
return GoVersionInfo{Version: greatestVersion, Found: true}
84+
}
85+
}
86+
87+
return GoVersionInfo{Version: "", Found: false}
88+
}
89+
5790
// Determines whether any of the directory paths in the input are nested.
5891
func checkDirsNested(inputDirs []string) (string, bool) {
5992
// replace "." with "" so that we can check if all the paths are nested
@@ -310,8 +343,8 @@ func getBuildRoots(emitDiagnostics bool) (goWorkspaces []GoWorkspace, totalModul
310343
return
311344
}
312345

313-
// Returns the appropriate DependencyInstallerMode for the current project
314-
func getDepMode(emitDiagnostics bool) []GoWorkspace {
346+
// Finds Go workspaces in the current working directory.
347+
func GetWorkspaceInfo(emitDiagnostics bool) []GoWorkspace {
315348
bazelPaths := util.FindAllFilesWithName(".", "BUILD", "vendor")
316349
bazelPaths = append(bazelPaths, util.FindAllFilesWithName(".", "BUILD.bazel", "vendor")...)
317350
if len(bazelPaths) > 0 {
@@ -400,37 +433,18 @@ func getModMode(depMode DependencyInstallerMode, baseDir string) ModMode {
400433
return ModUnset
401434
}
402435

403-
type BuildInfo struct {
404-
DepMode DependencyInstallerMode
405-
ModMode ModMode
406-
BaseDir string
407-
}
408-
409-
func GetBuildInfo(emitDiagnostics bool) []BuildInfo {
410-
goWorkspaces := getDepMode(true)
411-
results := make([]BuildInfo, len(goWorkspaces))
412-
413-
for i, workspace := range goWorkspaces {
414-
modMode := getModMode(workspace.DepMode, workspace.BaseDir)
415-
results[i] = BuildInfo{workspace.DepMode, modMode, workspace.BaseDir}
416-
}
417-
418-
return results
419-
}
420-
421436
// Tries to open `go.mod` and read a go directive, returning the version and whether it was found.
422-
func TryReadGoDirective(buildInfo BuildInfo) GoVersionInfo {
423-
if buildInfo.DepMode == GoGetWithModules {
424-
versionRe := regexp.MustCompile(`(?m)^go[ \t\r]+([0-9]+\.[0-9]+(\.[0-9]+)?)$`)
425-
goMod, err := os.ReadFile(filepath.Join(buildInfo.BaseDir, "go.mod"))
426-
if err != nil {
427-
log.Println("Failed to read go.mod to check for missing Go version")
428-
} else {
429-
matches := versionRe.FindSubmatch(goMod)
430-
if matches != nil {
431-
if len(matches) > 1 {
432-
return GoVersionInfo{string(matches[1]), true}
433-
}
437+
// The version string is returned in the "1.2.3" format.
438+
func tryReadGoDirective(path string) GoVersionInfo {
439+
versionRe := regexp.MustCompile(`(?m)^go[ \t\r]+([0-9]+\.[0-9]+(\.[0-9]+)?)$`)
440+
goMod, err := os.ReadFile(path)
441+
if err != nil {
442+
log.Println("Failed to read go.mod to check for missing Go version")
443+
} else {
444+
matches := versionRe.FindSubmatch(goMod)
445+
if matches != nil {
446+
if len(matches) > 1 {
447+
return GoVersionInfo{string(matches[1]), true}
434448
}
435449
}
436450
}

0 commit comments

Comments
 (0)