github.com/nya3jp/tast@v0.0.0-20230601000426-85c8e4d83a9b/src/go.chromium.org/tast/core/internal/crosbundle/software.go (about) 1 // Copyright 2021 The ChromiumOS Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 package crosbundle 6 7 import ( 8 "bufio" 9 "context" 10 "fmt" 11 "os" 12 "sort" 13 "strings" 14 15 "go.chromium.org/tast/core/autocaps" 16 "go.chromium.org/tast/core/errors" 17 "go.chromium.org/tast/core/internal/expr" 18 "go.chromium.org/tast/core/internal/logging" 19 "go.chromium.org/tast/core/lsbrelease" 20 21 protocol "go.chromium.org/tast/core/framework/protocol" 22 ) 23 24 const ( 25 // autotestCapPrefix is the prefix for autotest-capability feature names. 26 autotestCapPrefix = "autotest-capability:" 27 28 // useFlagsFile is the path to the file containing USE flags enabled for 29 // the board. 30 // The tast-use-flags package attempts to install this file to /etc, 31 // but it gets diverted to /usr/local since it's installed for test images. 32 useFlagsFile = "/usr/local/etc/tast_use_flags.txt" 33 ) 34 35 // detectSoftwareFeatures implements the main function of RunnerGetDUTInfoMode (i.e., except input/output 36 // conversion for RPC). 37 func detectSoftwareFeatures(ctx context.Context, extraUSEFlags []string) (*protocol.SoftwareFeatures, error) { 38 // If the file listing USE flags doesn't exist, we're probably running on a non-test 39 // image. Return an empty response to signal that to the caller. 40 if _, err := os.Stat(useFlagsFile); os.IsNotExist(err) { 41 return nil, nil 42 } 43 44 flags, err := readUSEFlagsFile(useFlagsFile) 45 if err != nil { 46 return nil, errors.Wrapf(err, "failed to read %v", useFlagsFile) 47 } 48 flags = append(flags, extraUSEFlags...) 49 50 if lr, err := lsbrelease.LoadFrom(lsbrelease.Path); err != nil { 51 logging.Infof(ctx, "Failed to read lsbrelease; board names in software feature definitions will not work: %v", err) 52 } else if board, ok := lr[lsbrelease.Board]; !ok { 53 logging.Infof(ctx, "Failed to find boardname in lsbrelease; board names in software feature definitions will not work") 54 } else { 55 flags = append(flags, "board:"+board) 56 } 57 58 autotestCaps, err := autocaps.Read(autocaps.DefaultCapabilityDir, nil) 59 if err != nil { 60 logging.Infof(ctx, "%s: %v", autocaps.DefaultCapabilityDir, err) 61 62 } 63 64 features, err := determineSoftwareFeatures(softwareFeatureDefs, flags, autotestCaps) 65 if err != nil { 66 return nil, err 67 } 68 return features, nil 69 } 70 71 // readUSEFlagsFile reads a list of USE flags from fn (see StaticConfig.USEFlagsFile). 72 // Each flag should be specified on its own line, and lines beginning with '#' are ignored. 73 func readUSEFlagsFile(fn string) ([]string, error) { 74 f, err := os.Open(fn) 75 if err != nil { 76 return nil, err 77 } 78 defer f.Close() 79 80 var flags []string 81 sc := bufio.NewScanner(f) 82 for sc.Scan() { 83 flag := strings.TrimSpace(sc.Text()) 84 if flag != "" && flag[0] != '#' { 85 flags = append(flags, flag) 86 } 87 } 88 if err = sc.Err(); err != nil { 89 return nil, err 90 } 91 return flags, nil 92 } 93 94 // determineSoftwareFeatures computes the DUT's available and unavailable software features. 95 // definitions maps feature names to definitions (see StaticConfig.SoftwareFeatureDefinitions). 96 // useFlags contains a list of relevant USE flags that were set when building the system image (see StaticConfig.USEFlagsFile). 97 // autotestCaps contains a mapping from autotest-capability names to the corresponding states. 98 func determineSoftwareFeatures(definitions map[string]string, useFlags []string, autotestCaps map[string]autocaps.State) ( 99 *protocol.SoftwareFeatures, error) { 100 var available, unavailable []string 101 for ft, es := range definitions { 102 if strings.HasPrefix(ft, autotestCapPrefix) { 103 return nil, fmt.Errorf("feature %q has reserved prefix %q", ft, autotestCapPrefix) 104 } 105 106 ex, err := expr.New(es) 107 if err != nil { 108 return nil, fmt.Errorf("failed to parse %q feature expression %q: %v", ft, es, err) 109 } 110 if ex.Matches(useFlags) { 111 available = append(available, ft) 112 } else { 113 unavailable = append(unavailable, ft) 114 } 115 } 116 117 for name, state := range autotestCaps { 118 if state == autocaps.Yes { 119 available = append(available, autotestCapPrefix+name) 120 } else { 121 unavailable = append(unavailable, autotestCapPrefix+name) 122 } 123 } 124 125 sort.Strings(available) 126 sort.Strings(unavailable) 127 return &protocol.SoftwareFeatures{Available: available, Unavailable: unavailable}, nil 128 }