github.com/joey-fossa/fossa-cli@v0.7.34-0.20190708193710-569f1e8679f0/analyzers/buck/buck.go (about) 1 // Package buck implements the analyzer for Buck. https://buckbuild.com 2 // 3 // A `BuildTarget` in Buck is defined as a Build Target by Buck which is in 4 // in the format of `//src/build:target`. Buck defines this as a string used to 5 // identify a Build Rule. 6 // 7 // Understanding target patterns is helpful to understand how buck projects are built 8 // and managed. Documentation can be found on Bazel's website here: 9 // https://docs.bazel.build/versions/master/guide.html#target-patterns 10 // 11 // This package is implemented by externally calling the `buck` build tool. 12 // 13 // FAQ 14 // 15 // 1. Why is analyzing manifest files not a supported strategy as it is for other tools? 16 // 17 // `.buckconfig` can be used to discover cells but the `repository` field which 18 // defines cells is not required . 19 // `BUCK` files are written in Skylark (a dialect of Python) and are impossible to statically analyze. 20 // `buck audit` provides json formatted data for dependency and input information. 21 package buck 22 23 import ( 24 "os" 25 "path" 26 "strings" 27 28 "github.com/apex/log" 29 "github.com/pkg/errors" 30 "gopkg.in/go-ini/ini.v1" 31 32 "github.com/fossas/fossa-cli/buildtools/buck" 33 "github.com/fossas/fossa-cli/exec" 34 "github.com/fossas/fossa-cli/files" 35 "github.com/fossas/fossa-cli/graph" 36 "github.com/fossas/fossa-cli/module" 37 "github.com/fossas/fossa-cli/pkg" 38 ) 39 40 // Analyzer defines a Buck analyzer. 41 type Analyzer struct { 42 Module module.Module 43 Upload bool 44 Setup buck.Buck 45 } 46 47 // New constructs a new Buck analyzer from a module. 48 func New(module module.Module) (*Analyzer, error) { 49 buckCmd, _, err := exec.Which("--version", os.Getenv("FOSSA_BUCK_CMD"), "./buckw", "buck") 50 if err != nil { 51 return nil, err 52 } 53 54 analyzer := Analyzer{ 55 Module: module, 56 Upload: true, 57 Setup: buck.New(module.BuildTarget, buckCmd), 58 } 59 return &analyzer, nil 60 } 61 62 // Clean is not implemented. 63 func (a *Analyzer) Clean() error { 64 return nil 65 } 66 67 // Build is not implemented. 68 func (a *Analyzer) Build() error { 69 return nil 70 } 71 72 // IsBuilt is not implemented. 73 func (a *Analyzer) IsBuilt() (bool, error) { 74 return true, nil 75 } 76 77 // Analyze analyzes a buck build target and its dependencies. 78 func (a *Analyzer) Analyze() (graph.Deps, error) { 79 return a.Setup.Deps(a.Upload) 80 } 81 82 // Discover is used to operate Discovery with a custom `buck` command. 83 func Discover(dir string, opts map[string]interface{}) ([]module.Module, error) { 84 buckCmd, _, err := exec.Which("--version", os.Getenv("FOSSA_BUCK_CMD"), "./buckw", "buck") 85 if err != nil { 86 return nil, err 87 } 88 89 return DiscoverWithCommand(dir, opts, buck.NewCmd(buckCmd)) 90 } 91 92 // DiscoverWithCommand finds a Buck project by first looking for a ".buckconfig" file and then a "BUCK" file. 93 // 1. ".buckconfig" file is found and we know that that we are at the root of a Buck cell. 94 // a. Attempt to find user defined aliases in .buckconfig. 95 // b. No aliases, run `buck targets //` to find all local targets. 96 // 2. "BUCK" file is found. 97 // a. Run `buck targets <directory>:` to find all local targets. 98 func DiscoverWithCommand(dir string, opts map[string]interface{}, buckCommand func(string, ...string) (string, error)) ([]module.Module, error) { 99 var moduleList []module.Module 100 buckConfig, err := files.Exists(dir, ".buckconfig") 101 102 if err == nil && buckConfig { 103 file, err := ini.Load(path.Join(dir, ".buckconfig")) 104 if err != nil { 105 return nil, errors.Errorf("Unable to read `.buckconfig`: %s", err) 106 } 107 108 aliases, err := file.GetSection("alias") 109 if err == nil && len(aliases.Keys()) > 0 { 110 for name, target := range aliases.KeysHash() { 111 moduleList = append(moduleList, newModule(name, target, dir)) 112 } 113 return moduleList, nil 114 } 115 116 out, err := buckCommand("targets", "//") 117 if err != nil { 118 return nil, err 119 } 120 121 buckTargetList := strings.Split(strings.TrimSpace(out), "\n") 122 for _, target := range buckTargetList { 123 moduleList = append(moduleList, newModule(target, target, dir)) 124 } 125 return moduleList, nil 126 } 127 128 buckFile, err := files.Exists(dir, "BUCK") 129 if err == nil && buckFile { 130 wd, err := os.Getwd() 131 if err != nil { 132 return nil, errors.Errorf("Cannot get working directory: %s", err) 133 } 134 135 buckRoot, err := buckCommand("root") 136 if err != nil { 137 return nil, err 138 } 139 140 // Condition the current directory to the format of "parent/child:" from the root directory. 141 buckDirectory := strings.TrimPrefix(wd, strings.TrimSpace(buckRoot)+"/") 142 out, err := buckCommand("targets", buckDirectory+":") 143 if err != nil { 144 return nil, err 145 } 146 147 targets := strings.Split(out, "\n") 148 for _, target := range targets { 149 if len(target) > 0 { 150 moduleList = append(moduleList, newModule(target, target, dir)) 151 } 152 } 153 } 154 return moduleList, nil 155 } 156 157 func newModule(name, target, dir string) module.Module { 158 log.WithFields(log.Fields{ 159 "path": dir, 160 "name": name, 161 }).Debug("constructing Buck module") 162 163 return module.Module{ 164 Name: name, 165 Type: pkg.Buck, 166 BuildTarget: target, 167 Dir: dir, 168 } 169 }