@@ -139,12 +139,18 @@ func listPackages(fileRoot, importRoot string) (PackageTree, error) {
139
139
140
140
// Skip dirs that are known to hold non-local/dependency code.
141
141
//
142
- // We don't skip .*, _*, or testdata dirs because, while it may be poor
143
- // form, it's not a compiler error to import them .
142
+ // We don't skip _*, or testdata dirs because, while it may be poor
143
+ // form, importing them is not a compilation error.
144
144
switch fi .Name () {
145
145
case "vendor" , "Godeps" :
146
146
return filepath .SkipDir
147
147
}
148
+ // We do skip dot-dirs, though, because it's such a ubiquitous standard
149
+ // that they not be visited by normal commands, and because things get
150
+ // really weird if we don't.
151
+ //
152
+ // TODO(sdboyer) does this entail that we should chuck dot-led import
153
+ // paths later on?
148
154
if strings .HasPrefix (fi .Name (), "." ) {
149
155
return filepath .SkipDir
150
156
}
@@ -391,6 +397,14 @@ func wmToReach(workmap map[string]wm, basedir string) map[string][]string {
391
397
// path is poisoned.
392
398
var clean bool
393
399
for in := range w .in {
400
+ // It's possible, albeit weird, for a package to import itself.
401
+ // If we try to visit self, though, then it erroneously poisons
402
+ // the path, as it would be interpreted as grey. In reality,
403
+ // this becomes a no-op, so just skip it.
404
+ if in == pkg {
405
+ continue
406
+ }
407
+
394
408
clean = dfe (in , path )
395
409
if ! clean {
396
410
// Path is poisoned. Our reachmap was already deleted by the
@@ -720,8 +734,8 @@ type PackageOrErr struct {
720
734
// transitively imported by the internal packages in the tree.
721
735
//
722
736
// main indicates whether (true) or not (false) to include main packages in the
723
- // analysis. main packages should generally be excluded when analyzing the
724
- // non- root dependency , as they inherently can't be imported.
737
+ // analysis. main packages are generally excluded when analyzing anything other
738
+ // than the root project , as they inherently can't be imported.
725
739
//
726
740
// tests indicates whether (true) or not (false) to include imports from test
727
741
// files in packages when computing the reach map.
@@ -826,9 +840,10 @@ func (t PackageTree) ExternalReach(main, tests bool, ignore map[string]bool) map
826
840
//
827
841
// If an internal path is ignored, all of the external packages that it uniquely
828
842
// imports are omitted. Note, however, that no internal transitivity checks are
829
- // made here - every non-ignored package in the tree is considered
830
- // independently. That means, given a PackageTree with root A and packages at A,
831
- // A/foo, and A/bar, and the following import chain:
843
+ // made here - every non-ignored package in the tree is considered independently
844
+ // (with one set of exceptions, noted below). That means, given a PackageTree
845
+ // with root A and packages at A, A/foo, and A/bar, and the following import
846
+ // chain:
832
847
//
833
848
// A -> A/foo -> A/bar -> B/baz
834
849
//
@@ -854,50 +869,64 @@ func (t PackageTree) ExternalReach(main, tests bool, ignore map[string]bool) map
854
869
// consideration; neither B/foo nor B/baz will be in the results. If A/bar, with
855
870
// its errors, is ignored, however, then A will remain, and B/foo will be in the
856
871
// results.
857
- func (t PackageTree ) ListExternalImports (main , tests bool , ignore map [string ]bool ) ([]string , error ) {
858
- var someerrs bool
859
- exm := make (map [string ]struct {})
860
-
861
- if ignore == nil {
862
- ignore = make (map [string ]bool )
863
- }
864
-
865
- var imps []string
866
- for ip , perr := range t .Packages {
867
- if perr .Err != nil {
868
- someerrs = true
869
- continue
870
- }
871
-
872
- p := perr .P
873
- // Skip main packages, unless param says otherwise
874
- if p .Name == "main" && ! main {
875
- continue
876
- }
877
- // Skip ignored packages
878
- if ignore [ip ] {
879
- continue
880
- }
872
+ //
873
+ // Finally, note that if a directory is named "testdata", or has a leading dot
874
+ // or underscore, it will not be directly analyzed as a source. This is in
875
+ // keeping with Go tooling conventions that such directories should be ignored.
876
+ // So, if:
877
+ //
878
+ // A -> B/foo
879
+ // A/.bar -> B/baz
880
+ // A/_qux -> B/baz
881
+ // A/testdata -> B/baz
882
+ //
883
+ // Then B/foo will be returned, but B/baz will not, because all three of the
884
+ // packages that import it are in directories with disallowed names.
885
+ //
886
+ // HOWEVER, in keeping with the Go compiler, if one of those packages in a
887
+ // disallowed directory is imported by a package in an allowed directory, then
888
+ // it *will* be used. That is, while tools like go list will ignore a directory
889
+ // named .foo, you can still import from .foo. Thus, it must be included. So,
890
+ // if:
891
+ //
892
+ // -> B/foo
893
+ // /
894
+ // A
895
+ // \
896
+ // -> A/.bar -> B/baz
897
+ //
898
+ // A is legal, and it imports A/.bar, so the results will include B/baz.
899
+ func (t PackageTree ) ListExternalImports (main , tests bool , ignore map [string ]bool ) []string {
900
+ // First, we need a reachmap
901
+ rm := t .ExternalReach (main , tests , ignore )
881
902
882
- imps = imps [:0 ]
883
- imps = p .Imports
884
- if tests {
885
- imps = dedupeStrings (imps , p .TestImports )
903
+ exm := make (map [string ]struct {})
904
+ for pkg , reach := range rm {
905
+ // Eliminate import paths with any elements having leading dots, leading
906
+ // underscores, or testdata. If these are internally reachable (which is
907
+ // a no-no, but possible), any external imports will have already been
908
+ // pulled up through ExternalReach. The key here is that we don't want
909
+ // to treat such packages as themselves being sources.
910
+ //
911
+ // TODO(sdboyer) strings.Split will always heap alloc, which isn't great to do
912
+ // in a loop like this. We could also just parse it ourselves...
913
+ var skip bool
914
+ for _ , elem := range strings .Split (pkg , "/" ) {
915
+ if strings .HasPrefix (elem , "." ) || strings .HasPrefix (elem , "_" ) || elem == "testdata" {
916
+ skip = true
917
+ break
918
+ }
886
919
}
887
920
888
- for _ , imp := range imps {
889
- if ! checkPrefixSlash ( filepath . Clean ( imp ), t . ImportRoot ) && ! ignore [ imp ] {
890
- exm [imp ] = struct {}{}
921
+ if ! skip {
922
+ for _ , ex := range reach {
923
+ exm [ex ] = struct {}{}
891
924
}
892
925
}
893
926
}
894
927
895
928
if len (exm ) == 0 {
896
- if someerrs {
897
- // TODO(sdboyer) proper errs
898
- return nil , fmt .Errorf ("No packages without errors in %s" , t .ImportRoot )
899
- }
900
- return nil , nil
929
+ return nil
901
930
}
902
931
903
932
ex := make ([]string , len (exm ))
@@ -908,7 +937,7 @@ func (t PackageTree) ListExternalImports(main, tests bool, ignore map[string]boo
908
937
}
909
938
910
939
sort .Strings (ex )
911
- return ex , nil
940
+ return ex
912
941
}
913
942
914
943
// checkPrefixSlash checks to see if the prefix is a prefix of the string as-is,
0 commit comments