diff --git a/internal/build/build.go b/internal/build/build.go index d2a77cbb..f283130a 100644 --- a/internal/build/build.go +++ b/internal/build/build.go @@ -106,8 +106,7 @@ func Do(args []string, conf *Config) { prog := llssa.NewProgram(nil) sizes := prog.TypeSizes - // dedup := packages.NewDeduper() - dedup := (*packages.Deduper)(nil) + dedup := packages.NewDeduper() if patterns == nil { patterns = []string{"."} diff --git a/internal/packages/load.go b/internal/packages/load.go index 22e4fbb6..49b81d8f 100644 --- a/internal/packages/load.go +++ b/internal/packages/load.go @@ -17,14 +17,12 @@ package packages import ( - "errors" "fmt" "go/types" "runtime" "sync" "unsafe" - "golang.org/x/sync/errgroup" "golang.org/x/tools/go/packages" ) @@ -59,12 +57,6 @@ const ( // Calls to Load do not modify this struct. type Config = packages.Config -func setGoListOverlayFile(cfg *Config, val string) { - // TODO(xsw): suppose that the field is at the end of the struct - ptr := uintptr(unsafe.Pointer(cfg)) + (unsafe.Sizeof(*cfg) - unsafe.Sizeof(val)) - *(*string)(unsafe.Pointer(ptr)) = val -} - // A Package describes a loaded Go package. type Package = packages.Package @@ -86,127 +78,15 @@ type loader struct { requestedMode LoadMode } -// Deduper wraps a DriverResponse, deduplicating its contents. type Deduper struct { - seenRoots map[string]bool - seenPackages map[string]*Package - dr *packages.DriverResponse // TODO(xsw): ensure offset of dr } -//go:linkname NewDeduper golang.org/x/tools/go/packages.newDeduper -func NewDeduper() *Deduper - -//go:linkname addAll golang.org/x/tools/go/packages.(*responseDeduper).addAll -func addAll(r *Deduper, dr *packages.DriverResponse) - -func mergeResponsesEx(dedup *Deduper, responses ...*packages.DriverResponse) *packages.DriverResponse { - if len(responses) == 0 { - return nil - } - if dedup == nil { - dedup = NewDeduper() - } - response := dedup - response.dr.NotHandled = false - response.dr.Compiler = responses[0].Compiler - response.dr.Arch = responses[0].Arch - response.dr.GoVersion = responses[0].GoVersion - for _, v := range responses { - addAll(response, v) - } - return response.dr +func NewDeduper() *Deduper { + return nil } -// driver is the type for functions that query the build system for the -// packages named by the patterns. -type driver func(cfg *Config, patterns ...string) (*packages.DriverResponse, error) - -func callDriverOnChunksEx(dedup *Deduper, driver driver, cfg *Config, chunks [][]string) (*packages.DriverResponse, error) { - if len(chunks) == 0 { - return driver(cfg) - } - responses := make([]*packages.DriverResponse, len(chunks)) - errNotHandled := errors.New("driver returned NotHandled") - var g errgroup.Group - for i, chunk := range chunks { - i := i - chunk := chunk - g.Go(func() (err error) { - responses[i], err = driver(cfg, chunk...) - if responses[i] != nil && responses[i].NotHandled { - err = errNotHandled - } - return err - }) - } - if err := g.Wait(); err != nil { - if errors.Is(err, errNotHandled) { - return &packages.DriverResponse{NotHandled: true}, nil - } - return nil, err - } - return mergeResponsesEx(dedup, responses...), nil -} - -//go:linkname splitIntoChunks golang.org/x/tools/go/packages.splitIntoChunks -func splitIntoChunks(patterns []string, argMax int) ([][]string, error) - -//go:linkname findExternalDriver golang.org/x/tools/go/packages.findExternalDriver -func findExternalDriver(cfg *Config) driver - -//go:linkname goListDriver golang.org/x/tools/go/packages.goListDriver -func goListDriver(cfg *Config, patterns ...string) (_ *packages.DriverResponse, err error) - -//go:linkname writeOverlays golang.org/x/tools/internal/gocommand.WriteOverlays -func writeOverlays(overlay map[string][]byte) (filename string, cleanup func(), err error) - -func defaultDriverEx(dedup *Deduper, cfg *Config, patterns ...string) (*packages.DriverResponse, bool, error) { - const ( - // windowsArgMax specifies the maximum command line length for - // the Windows' CreateProcess function. - windowsArgMax = 32767 - // maxEnvSize is a very rough estimation of the maximum environment - // size of a user. - maxEnvSize = 16384 - // safeArgMax specifies the maximum safe command line length to use - // by the underlying driver excl. the environment. We choose the Windows' - // ARG_MAX as the starting point because it's one of the lowest ARG_MAX - // constants out of the different supported platforms, - // e.g., https://www.in-ulm.de/~mascheck/various/argmax/#results. - safeArgMax = windowsArgMax - maxEnvSize - ) - chunks, err := splitIntoChunks(patterns, safeArgMax) - if err != nil { - return nil, false, err - } - - if driver := findExternalDriver(cfg); driver != nil { - response, err := callDriverOnChunksEx(dedup, driver, cfg, chunks) - if err != nil { - return nil, false, err - } else if !response.NotHandled { - return response, true, nil - } - // (fall through) - } - - // go list fallback - // - // Write overlays once, as there are many calls - // to 'go list' (one per chunk plus others too). - overlay, cleanupOverlay, err := writeOverlays(cfg.Overlay) - if err != nil { - return nil, false, err - } - defer cleanupOverlay() - setGoListOverlayFile(cfg, overlay) - - response, err := callDriverOnChunksEx(dedup, goListDriver, cfg, chunks) - if err != nil { - return nil, false, err - } - return response, false, err -} +//go:linkname defaultDriver golang.org/x/tools/go/packages.defaultDriver +func defaultDriver(cfg *Config, patterns ...string) (*packages.DriverResponse, bool, error) //go:linkname newLoader golang.org/x/tools/go/packages.newLoader func newLoader(cfg *Config) *loader @@ -230,7 +110,7 @@ func refine(ld *loader, response *packages.DriverResponse) ([]*Package, error) // provided for convenient display of all errors. func LoadEx(dedup *Deduper, sizes func(types.Sizes) types.Sizes, cfg *Config, patterns ...string) ([]*Package, error) { ld := newLoader(cfg) - response, external, err := defaultDriverEx(dedup, &ld.Config, patterns...) + response, external, err := defaultDriver(&ld.Config, patterns...) if err != nil { return nil, err }