Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  glob_cache.go   Sprache: unbekannt

 
Spracherkennung für: .go vermutete Sprache: Unknown {[0] [0] [0]} [Methode: Schwerpunktbildung, einfache Gewichte, sechs Dimensionen]

// Copyright 2025 Google Inc. All rights reserved.
//
// 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 pathtools

import (
 "bufio"
 "encoding/binary"
 "errors"
 "fmt"
 "io"
 "iter"
 "os"
 "runtime"
 "slices"
 "strings"
 "sync"
 "syscall"
 "unsafe"
)

// globFileMagic is the header used at the top of the glob cache file.  Any change to the
// on-disk format of the cache should be accompanied by a change to the magic string to
// avoid exposing the decoder to an unexpected format.
const globFileMagic = "Soong glob file version 1\n"

// globFileEncoder writes a list of globs to a cache file.  It's helpers are conceptually
// similar to the ones in the gobtools package, and the encoder is similar to the
// ones generated by gob_gen, but those use a *bytes.Buffer, while these write directly
// to a buffered file.
type globFileEncoder struct {
 w *bufio.Writer
}

func newGlobFileEncoder(w *bufio.Writer) (*globFileEncoder, error) {
 _, err := w.WriteString(globFileMagic)
 if err != nil {
  return nil, fmt.Errorf("failed to write glob file magic: %w", err)
 }

 return &globFileEncoder{w: w}, nil
}

func (e *globFileEncoder) writeVarint(v int) error {
 var buf [binary.MaxVarintLen64]byte
 n := binary.PutVarint(buf[:], int64(v))
 _, err := e.w.Write(buf[:n])
 return err
}

func (e *globFileEncoder) writeString(s string) error {
 err := e.writeVarint(len(s))
 if err != nil {
  return err
 }
 _, err = e.w.WriteString(s)
 return err
}

func (e *globFileEncoder) writeStringList(s []string) error {
 err := e.writeVarint(len(s))
 if err != nil {
  return err
 }
 for _, str := range s {
  err := e.writeString(str)
  if err != nil {
   return err
  }
 }
 return nil
}

func (e *globFileEncoder) encode(globs []GlobResult) error {
 err := e.writeVarint(len(globs))
 if err != nil {
  return err
 }
 for _, glob := range globs {
  err := e.writeString(glob.Pattern)
  if err != nil {
   return err
  }
  err = e.writeStringList(glob.Excludes)
  if err != nil {
   return err
  }
  err = e.writeStringList(glob.Matches)
  if err != nil {
   return err
  }
  err = e.writeStringList(glob.Deps)
  if err != nil {
   return err
  }
 }
 return nil
}

type globFileDecoder struct {
 r *bufio.Reader
 n int
}

var errBadMagic = errors.New("bad glob file magic")

func newGlobFileDecoder(r *bufio.Reader) (*globFileDecoder, error) {
 buf := make([]byte, len(globFileMagic))
 _, err := io.ReadFull(r, buf)
 if errors.Is(err, io.ErrUnexpectedEOF) || errors.Is(err, io.EOF) {
  return nil, errBadMagic
 } else if err != nil {
  return nil, fmt.Errorf("failed to read glob file magic: %w", err)
 } else if string(buf) != globFileMagic {
  return nil, errBadMagic
 }

 d := &globFileDecoder{r: r}
 n, err := d.readVarint()
 if err != nil {
  return nil, err
 }
 d.n = n
 return d, nil
}

func (d *globFileDecoder) readVarint() (int, error) {
 n, err := binary.ReadVarint(d.r)
 return int(n), err
}

func (d *globFileDecoder) readString() (string, error) {
 n, err := d.readVarint()
 if err != nil {
  return "", err
 }
 b := make([]byte, n)
 _, err = io.ReadFull(d.r, b)
 if err != nil {
  return "", err
 }
 return unsafe.String(unsafe.SliceData(b), len(b)), nil
}

func (d *globFileDecoder) readStringList() ([]string, error) {
 n, err := d.readVarint()
 if err != nil {
  return nil, err
 }
 if n == 0 {
  return nil, nil
 }
 list := make([]string, 0, n)
 for _ = range n {
  s, err := d.readString()
  if err != nil {
   return nil, err
  }
  list = append(list, s)
 }

 return list, nil
}

func (d *globFileDecoder) more() bool {
 return d.n > 0
}

func (d *globFileDecoder) decode() (GlobResult, error) {
 var glob GlobResult
 var err error
 d.n -= 1
 glob.Pattern, err = d.readString()
 if err != nil {
  return GlobResult{}, err
 }
 glob.Excludes, err = d.readStringList()
 if err != nil {
  return GlobResult{}, err
 }
 glob.Matches, err = d.readStringList()
 if err != nil {
  return GlobResult{}, err
 }
 glob.Deps, err = d.readStringList()
 if err != nil {
  return GlobResult{}, err
 }
 return glob, err
}

func (d *globFileDecoder) iter() (iter.Seq[GlobResult], func() error) {
 var err error
 return func(yield func(GlobResult) bool) {
   for d.more() {
    var glob GlobResult
    glob, err = d.decode()
    if err != nil {
     return
    }
    if !yield(glob) {
     return
    }
   }
  }, func() error {
   return err
  }
}

// WriteGlobFile writes a list of glob results to a cache file for later use by CheckForChangedGlobs.
func WriteGlobFile(w io.Writer, globs []GlobResult) (err error) {
 bw := bufio.NewWriterSize(w, 16*1024*1024)
 defer func() {
  flushErr := bw.Flush()
  if err != nil {
   err = flushErr
  }
 }()
 encoder, err := newGlobFileEncoder(bw)
 if err != nil {
  return err
 }
 return encoder.encode(globs)
}

// checkIfGlobDepsChanged returns true if any of the Deps of the cachedGlob have a timestamp newer than
// globsTimeMicros, do not exist, or have a parent that is not a directory.
func checkIfGlobDepsChanged(fs FileSystem, cachedGlob GlobResult, globsTimeMicros int64) (bool, error) {
 for _, dep := range cachedGlob.Deps {
  info, err := fs.Stat(dep)
  if errors.Is(err, os.ErrNotExist) || errors.Is(err, syscall.ENOTDIR) {
   return true, nil
  } else if err != nil {
   return true, err
  }
  if info.ModTime().UnixMicro() > globsTimeMicros {
   return true, nil
  }
 }
 return false, nil
}

// checkIfGlobChanged checks if a glob result has changed from the cached value, first by checking
// the previously recursed directories against a global timestamp to see if they have changed, then
// by rerunning the glob if necessary.  Returns true and a helpful error message if the glob has
// changed or an error occurred, or false if the glob has not changed.
func checkIfGlobChanged(fs FileSystem, cachedGlob GlobResult, globsTimeMicros int64) (bool, error) {
 // First, check if any of the deps are newer than the last time globs were checked.
 // If not, we don't need to rerun the glob.
 if hasNewDep, err := checkIfGlobDepsChanged(fs, cachedGlob, globsTimeMicros); err != nil {
  return true, err
 } else if !hasNewDep {
  return false, nil
 }

 // Then rerun the glob and check if we got the same result as before.
 result, err := glob(fs, cachedGlob.Pattern, cachedGlob.Excludes, FollowSymlinks)
 if err != nil {
  return true, err
 } else {
  if !slices.Equal(result.Matches, cachedGlob.Matches) {
   changedGlobName := result.Pattern
   if len(result.Excludes) > 2 {
    changedGlobName += fmt.Sprintf(" (excluding %d other patterns)", len(result.Excludes))
   } else if len(result.Excludes) > 0 {
    changedGlobName += " (excluding " + strings.Join(result.Excludes, " and ") + ")"
   }
   return true, fmt.Errorf("One culprit glob (may be more): %s", changedGlobName)
  }
 }

 return false, nil
}

// CheckForChangedGlobs returns true if any of the globs stored in the cache file  have changed
// the list of files they return.  It optimizes the check to finish in a few hundred ms even when
// there are tens of thousands of globs in the cache by first checking the modification time of
// any directories that were recursed during the original glob runs, and only rerunning globs
// whose relevant directories have been modified.
//
// All errors are treated as finding modified globs in order to gracefully fall back to always
// rerunning analysis.  If a modified glob is found an error is also returned that contains
// a helpful message to print to the console.
func CheckForChangedGlobs(fs FileSystem, r io.Reader, globsTimeMicros int64) (bool, error) {
 globsFileDecoder, err := newGlobFileDecoder(bufio.NewReaderSize(r, 16*1024*1024))
 if err != nil {
  if errors.Is(err, errBadMagic) {
   return true, fmt.Errorf("glob cache version is out of date")
  }
  return true, fmt.Errorf("failed to create glob file decoder: %w", err)
 }

 globsChan := make(chan GlobResult)
 wg := sync.WaitGroup{}

 hasChangedGlobs := false
 var errFromCheckGlobMutex sync.Mutex
 var errFromCheckGlob error

 for i := 0; i < runtime.NumCPU()*2; i++ {
  wg.Add(1)
  go func() {
   for cachedGlob := range globsChan {
    // If we've already determined we have changed globs, just finish consuming
    // the channel without doing any more checks.
    if hasChangedGlobs {
     continue
    }
    changed, err := checkIfGlobChanged(fs, cachedGlob, globsTimeMicros)
    if err != nil {
     errFromCheckGlobMutex.Lock()
     errFromCheckGlob = err
     errFromCheckGlobMutex.Unlock()
    }
    if changed {
     hasChangedGlobs = true
    }
   }
   wg.Done()
  }()
 }

 globsFileIter, errFromDecoder := globsFileDecoder.iter()
 for globs := range globsFileIter {
  globsChan <- globs
 }

 close(globsChan)
 wg.Wait()

 if errFromCheckGlob != nil {
  return true, errFromCheckGlob
 }

 if errFromDecoder() != nil {
  return true, fmt.Errorf("failed to decode glob file: %w", errFromCheckGlob)
 }

 return false, nil
}

type RestoredGlobsMetrics struct {
 CachedGlobs   uint64
 RestoredGlobs uint64
}

// RestoreGlobsFromCache returns a list of globs loaded from the glob cache whose dependencies are unchanged.
func RestoreGlobsFromCache(fs FileSystem, r io.Reader, globsTimeMicros int64) ([]GlobResult, RestoredGlobsMetrics, error) {
 metrics := RestoredGlobsMetrics{}
 globsFileDecoder, err := newGlobFileDecoder(bufio.NewReaderSize(r, 16*1024*1024))
 if err != nil {
  if errors.Is(err, errBadMagic) {
   return nil, metrics, nil
  }
  return nil, metrics, fmt.Errorf("failed to create glob file decoder: %w", err)
 }

 cachedGlobsChan := make(chan GlobResult)
 restoredGlobsChan := make(chan GlobResult)
 errChan := make(chan error)

 var errFromCheckGlob error

 wg := sync.WaitGroup{}

 for i := 0; i < runtime.NumCPU()*2; i++ {
  wg.Add(1)
  go func() {
   for cachedGlob := range cachedGlobsChan {
    changed, err := checkIfGlobDepsChanged(fs, cachedGlob, globsTimeMicros)
    if err != nil {
     errChan <- err
    } else if !changed {
     restoredGlobsChan <- cachedGlob
    }
   }
   wg.Done()
  }()
 }

 var restoredGlobs []GlobResult
 doneCh := make(chan bool)
 go func() {
  for {
   select {
   case err := <-errChan:
    if errFromCheckGlob == nil {
     errFromCheckGlob = err
    }
   case restoredGlob := <-restoredGlobsChan:
    restoredGlobs = append(restoredGlobs, restoredGlob)
   case <-doneCh:
    return
   }
  }
 }()

 globsFileIter, errFromDecoder := globsFileDecoder.iter()
 for globs := range globsFileIter {
  metrics.CachedGlobs++
  cachedGlobsChan <- globs
 }
 close(cachedGlobsChan)

 wg.Wait()
 close(doneCh)

 if errFromCheckGlob != nil {
  return nil, metrics, errFromCheckGlob
 }

 if errFromDecoder() != nil {
  return nil, metrics, fmt.Errorf("failed to decode glob file: %w", errFromCheckGlob)
 }

 metrics.RestoredGlobs = uint64(len(restoredGlobs))

 return restoredGlobs, metrics, nil
}

[Dauer der Verarbeitung: 0.1 Sekunden, vorverarbeitet 2026-06-28]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik