@@ -10,6 +10,7 @@ import (
1010 "path/filepath"
1111 "regexp"
1212 "runtime"
13+ "sort"
1314 "strings"
1415
1516 "golang.org/x/mod/semver"
@@ -240,28 +241,109 @@ func getSourceDir() string {
240241 return srcdir
241242}
242243
244+ func getDirs (paths []string ) []string {
245+ dirs := make ([]string , len (paths ))
246+ for i , path := range paths {
247+ dirs [i ] = filepath .Dir (path )
248+ }
249+ return dirs
250+ }
251+
252+ // Note this has the side effect of sorting `dirs`
253+ func checkDirsNested (dirs []string ) bool {
254+ // the paths were generated by a depth-first search so I think they might
255+ // be sorted, but we sort them just in case
256+ sort .Strings (dirs )
257+ for _ , dir := range dirs {
258+ if ! strings .HasPrefix (dir , dirs [0 ]) {
259+ return false
260+ }
261+ }
262+ return true
263+ }
264+
265+ func findGoModFiles (emitDiagnostics bool ) (string , bool ) {
266+ goModPaths := util .FindAllFilesWithName ("." , "go.mod" , "vendor" )
267+ if len (goModPaths ) == 0 {
268+ // preserve current behaviour
269+ return "." , false
270+ }
271+ goModDirs := getDirs (goModPaths )
272+ if util .AnyGoFilesOutsideDirs ("." , goModDirs ... ) {
273+ if emitDiagnostics {
274+ diagnostics .EmitGoFilesOutsideGoModules (goModPaths )
275+ }
276+ // preserve current behaviour
277+ return "." , true
278+ }
279+ if len (goModPaths ) > 1 {
280+ // currently not supported
281+ if emitDiagnostics {
282+ if checkDirsNested (goModDirs ) {
283+ diagnostics .EmitMultipleGoModFoundNested (goModPaths )
284+ } else {
285+ diagnostics .EmitMultipleGoModFoundNotNested (goModPaths )
286+ }
287+ }
288+ // preserve current behaviour
289+ return "." , true
290+ }
291+ if emitDiagnostics {
292+ if goModDirs [0 ] == "." {
293+ diagnostics .EmitSingleRootGoModFound (goModPaths [0 ])
294+ } else {
295+ diagnostics .EmitSingleNonRootGoModFound (goModPaths [0 ])
296+ }
297+ }
298+ return goModDirs [0 ], true
299+ }
300+
243301// Returns the appropriate DependencyInstallerMode for the current project
244- func getDepMode () DependencyInstallerMode {
245- if util .FileExists ("go.mod" ) {
302+ func getDepMode (emitDiagnostics bool ) (DependencyInstallerMode , string ) {
303+ if util .FileExists ("BUILD" ) {
304+ // currently not supported
305+ if emitDiagnostics {
306+ diagnostics .EmitBazelBuildFileFound ()
307+ }
308+ }
309+
310+ goWorkPaths := util .FindAllFilesWithName ("." , "go.work" , "vendor" )
311+ if len (goWorkPaths ) > 0 {
312+ // currently not supported
313+ if emitDiagnostics {
314+ diagnostics .EmitGoWorkFound (goWorkPaths )
315+ }
316+ }
317+
318+ baseDir , goModFound := findGoModFiles (emitDiagnostics )
319+ if goModFound {
246320 log .Println ("Found go.mod, enabling go modules" )
247- return GoGetWithModules
321+ return GoGetWithModules , baseDir
248322 }
323+
249324 if util .FileExists ("Gopkg.toml" ) {
325+ if emitDiagnostics {
326+ diagnostics .EmitGopkgTomlFound ()
327+ }
250328 log .Println ("Found Gopkg.toml, using dep instead of go get" )
251- return Dep
329+ return Dep , "."
252330 }
331+
253332 if util .FileExists ("glide.yaml" ) {
333+ if emitDiagnostics {
334+ diagnostics .EmitGlideYamlFound ()
335+ }
254336 log .Println ("Found glide.yaml, using Glide instead of go get" )
255- return Glide
337+ return Glide , "."
256338 }
257- return GoGetNoModules
339+ return GoGetNoModules , "."
258340}
259341
260342// Tries to open `go.mod` and read a go directive, returning the version and whether it was found.
261- func tryReadGoDirective (depMode DependencyInstallerMode ) (string , bool ) {
343+ func tryReadGoDirective (depMode DependencyInstallerMode , baseDir string ) (string , bool ) {
262344 if depMode == GoGetWithModules {
263345 versionRe := regexp .MustCompile (`(?m)^go[ \t\r]+([0-9]+\.[0-9]+)$` )
264- goMod , err := os .ReadFile ("go.mod" )
346+ goMod , err := os .ReadFile (filepath . Join ( baseDir , "go.mod" ) )
265347 if err != nil {
266348 log .Println ("Failed to read go.mod to check for missing Go version" )
267349 } else {
@@ -277,13 +359,13 @@ func tryReadGoDirective(depMode DependencyInstallerMode) (string, bool) {
277359}
278360
279361// Returns the appropriate ModMode for the current project
280- func getModMode (depMode DependencyInstallerMode ) ModMode {
362+ func getModMode (depMode DependencyInstallerMode , baseDir string ) ModMode {
281363 if depMode == GoGetWithModules {
282364 // if a vendor/modules.txt file exists, we assume that there are vendored Go dependencies, and
283365 // skip the dependency installation step and run the extractor with `-mod=vendor`
284- if util .FileExists (" vendor/modules.txt" ) {
366+ if util .FileExists (baseDir + "/ vendor/modules.txt" ) {
285367 return ModVendor
286- } else if util .DirExists (" vendor" ) {
368+ } else if util .DirExists (baseDir + "/ vendor" ) {
287369 return ModMod
288370 }
289371 }
@@ -344,25 +426,29 @@ func getNeedGopath(depMode DependencyInstallerMode, importpath string) bool {
344426}
345427
346428// Try to update `go.mod` and `go.sum` if the go version is >= 1.16.
347- func tryUpdateGoModAndGoSum (modMode ModMode , depMode DependencyInstallerMode ) {
429+ func tryUpdateGoModAndGoSum (modMode ModMode , depMode DependencyInstallerMode , baseDir string ) {
348430 // Go 1.16 and later won't automatically attempt to update go.mod / go.sum during package loading, so try to update them here:
349431 if modMode != ModVendor && depMode == GoGetWithModules && semver .Compare (getEnvGoSemVer (), "v1.16" ) >= 0 {
350432 // stat go.mod and go.sum
351- beforeGoModFileInfo , beforeGoModErr := os .Stat ("go.mod" )
433+ goModPath := filepath .Join (baseDir , "go.mod" )
434+ beforeGoModFileInfo , beforeGoModErr := os .Stat (goModPath )
352435 if beforeGoModErr != nil {
353436 log .Println ("Failed to stat go.mod before running `go mod tidy -e`" )
354437 }
355438
356- beforeGoSumFileInfo , beforeGoSumErr := os .Stat ("go.sum" )
439+ goSumPath := filepath .Join (baseDir , "go.sum" )
440+ beforeGoSumFileInfo , beforeGoSumErr := os .Stat (goSumPath )
357441
358442 // run `go mod tidy -e`
359- res := util .RunCmd (exec .Command ("go" , "mod" , "tidy" , "-e" ))
443+ cmd := exec .Command ("go" , "mod" , "tidy" , "-e" )
444+ cmd .Dir = baseDir
445+ res := util .RunCmd (cmd )
360446
361447 if ! res {
362448 log .Println ("Failed to run `go mod tidy -e`" )
363449 } else {
364450 if beforeGoModFileInfo != nil {
365- afterGoModFileInfo , afterGoModErr := os .Stat ("go.mod" )
451+ afterGoModFileInfo , afterGoModErr := os .Stat (goModPath )
366452 if afterGoModErr != nil {
367453 log .Println ("Failed to stat go.mod after running `go mod tidy -e`" )
368454 } else if afterGoModFileInfo .ModTime ().After (beforeGoModFileInfo .ModTime ()) {
@@ -371,7 +457,7 @@ func tryUpdateGoModAndGoSum(modMode ModMode, depMode DependencyInstallerMode) {
371457 }
372458 }
373459
374- afterGoSumFileInfo , afterGoSumErr := os .Stat ("go.sum" )
460+ afterGoSumFileInfo , afterGoSumErr := os .Stat (goSumPath )
375461 if afterGoSumErr != nil {
376462 log .Println ("Failed to stat go.sum after running `go mod tidy -e`" )
377463 } else {
@@ -560,7 +646,7 @@ func buildWithCustomCommands(inst string) {
560646}
561647
562648// Install dependencies using the given dependency installer mode.
563- func installDependencies (depMode DependencyInstallerMode ) {
649+ func installDependencies (depMode DependencyInstallerMode , baseDir string ) {
564650 // automatically determine command to install dependencies
565651 var install * exec.Cmd
566652 if depMode == Dep {
@@ -606,31 +692,28 @@ func installDependencies(depMode DependencyInstallerMode) {
606692
607693 // get dependencies
608694 install = exec .Command ("go" , "get" , "-v" , "./..." )
609- log .Println ("Installing dependencies using `go get -v ./...`." )
695+ install .Dir = baseDir
696+ log .Printf ("Installing dependencies using `go get -v ./...` in `%s`.\n " , baseDir )
610697 }
611698 util .RunCmd (install )
612699}
613700
614701// Run the extractor.
615- func extract (depMode DependencyInstallerMode , modMode ModMode ) {
702+ func extract (depMode DependencyInstallerMode , modMode ModMode , baseDir string ) {
616703 extractor , err := util .GetExtractorPath ()
617704 if err != nil {
618705 log .Fatalf ("Could not determine path of extractor: %v.\n " , err )
619706 }
620707
621- cwd , err := os .Getwd ()
622- if err != nil {
623- log .Fatalf ("Unable to determine current directory: %s\n " , err .Error ())
624- }
625-
626708 extractorArgs := []string {}
627709 if depMode == GoGetWithModules {
628710 extractorArgs = append (extractorArgs , modMode .argsForGoVersion (getEnvGoSemVer ())... )
629711 }
630712 extractorArgs = append (extractorArgs , "./..." )
631713
632- log .Printf ("Running extractor command '%s %v' from directory '%s'.\n " , extractor , extractorArgs , cwd )
714+ log .Printf ("Running extractor command '%s %v' from directory '%s'.\n " , extractor , extractorArgs , baseDir )
633715 cmd := exec .Command (extractor , extractorArgs ... )
716+ cmd .Dir = baseDir
634717 cmd .Stdout = os .Stdout
635718 cmd .Stderr = os .Stderr
636719 err = cmd .Run ()
@@ -650,21 +733,21 @@ func installDependenciesAndBuild() {
650733
651734 // determine how to install dependencies and whether a GOPATH needs to be set up before
652735 // extraction
653- depMode := getDepMode ()
736+ depMode , baseDir := getDepMode (true )
654737 if _ , present := os .LookupEnv ("GO111MODULE" ); ! present {
655738 os .Setenv ("GO111MODULE" , "auto" )
656739 }
657740
658- goModVersion , goModVersionFound := tryReadGoDirective (depMode )
741+ goModVersion , goModVersionFound := tryReadGoDirective (depMode , baseDir )
659742
660743 if semver .Compare ("v" + goModVersion , getEnvGoSemVer ()) >= 0 {
661744 diagnostics .EmitNewerGoVersionNeeded ()
662745 }
663746
664- modMode := getModMode (depMode )
747+ modMode := getModMode (depMode , baseDir )
665748 modMode = fixGoVendorIssues (modMode , depMode , goModVersionFound )
666749
667- tryUpdateGoModAndGoSum (modMode , depMode )
750+ tryUpdateGoModAndGoSum (modMode , depMode , baseDir )
668751
669752 importpath := getImportPath ()
670753 needGopath := getNeedGopath (depMode , importpath )
@@ -707,11 +790,11 @@ func installDependenciesAndBuild() {
707790 if modMode == ModVendor {
708791 log .Printf ("Skipping dependency installation because a Go vendor directory was found." )
709792 } else {
710- installDependencies (depMode )
793+ installDependencies (depMode , baseDir )
711794 }
712795 }
713796
714- extract (depMode , modMode )
797+ extract (depMode , modMode , baseDir )
715798}
716799
717800const minGoVersion = "1.11"
@@ -976,8 +1059,8 @@ func isGoInstalled() bool {
9761059// Get the version of Go to install and output it to stdout as json.
9771060func identifyEnvironment () {
9781061 var v versionInfo
979- depMode := getDepMode ()
980- v .goModVersion , v .goModVersionFound = tryReadGoDirective (depMode )
1062+ depMode , baseDir := getDepMode (false )
1063+ v .goModVersion , v .goModVersionFound = tryReadGoDirective (depMode , baseDir )
9811064
9821065 v .goEnvVersionFound = isGoInstalled ()
9831066 if v .goEnvVersionFound {
0 commit comments