| // Copyright 2022 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| package util |
| |
| import ( |
| "fmt" |
| "regexp" |
| "sort" |
| "strings" |
| ) |
| |
| type Target struct { |
| Name string `json:"name"` |
| AllDependentConfigs []string `json:"all_dependent_configs"` |
| Deps []string `json:"deps"` |
| Args []string `json:"args,omitempty"` |
| Inputs []string `json:"inputs,omitempty"` |
| Sources []string `json:"sources,omitempty"` |
| PublicConfigs []string `json:"public_configs,omitempty"` |
| IncludeDirs []string `json:"include_dirs,omitempty"` |
| Script string `json:"script,omitempty"` |
| LibDirs []string `json:"lib_dirs,omitempty"` |
| Libs []string `json:"libs,omitempty"` |
| Toolchain string `json:"toolchain,omitempty"` |
| |
| CleanNames []string `json:"cleanNames"` |
| CleanDeps []string `json:"cleanDeps"` |
| IsClean bool `json:"is_clean"` |
| |
| Children []*Target `json:"-"` |
| } |
| |
| func (t *Target) Clean(re *regexp.Regexp) error { |
| if t.IsClean { |
| return fmt.Errorf("Target '%s' is already clean", t.Name) |
| } |
| |
| paths := make([]string, 0) |
| paths = append(paths, t.Deps...) |
| paths = append(paths, t.Inputs...) |
| paths = append(paths, t.LibDirs...) |
| paths = append(paths, t.Libs...) |
| paths = append(paths, t.Sources...) |
| paths = append(paths, t.PublicConfigs...) |
| paths = append(paths, t.IncludeDirs...) |
| |
| // "include_dirs" entries often have a trailing slash. |
| // That trailing slash can cause string matching to fail later on. |
| for _, d := range t.IncludeDirs { |
| prefix, found := strings.CutSuffix(d, "/") |
| if found { |
| paths = append(paths, prefix) |
| } |
| } |
| |
| var err error |
| t.CleanNames, err = clean([]string{t.Name}, re) |
| if err != nil { |
| return err |
| } |
| |
| t.CleanDeps, err = clean(paths, re) |
| if err != nil { |
| return err |
| } |
| |
| t.IsClean = true |
| return nil |
| } |
| |
| // Converts a GN label string (such as those returned by Dependencies) and |
| // strips any target names and toolchains, thereby returning the directory |
| // of the label. |
| func clean(paths []string, re *regexp.Regexp) ([]string, error) { |
| set := make(map[string]bool, 0) |
| |
| for _, path := range paths { |
| thisSet := make(map[string]bool, 0) |
| thisSet[path] = true |
| |
| cutColon := true |
| if strings.Contains(path, "third_party/golibs") || strings.Contains(path, "rust_crates") { |
| cutColon = false |
| } |
| |
| // If this target isn't a rust crate target, we still want to retrieve |
| // the relevant directory, not the target name in that directory. |
| // If a colon exists in this string, delete it and everything after it. |
| if strings.Contains(path, ":") && cutColon { |
| thisSet[strings.Split(path, ":")[0]] = true |
| } |
| |
| // Same goes for toolchain definitions. |
| // If a parenthesis exists in this string, delete it and everything after it. |
| if strings.Contains(path, "(") { |
| thisSet[strings.Split(path, "(")[0]] = true |
| } |
| |
| // Many rust crate libraries have a version string in their target name, |
| // but no version string in their folder path. If we see this specific |
| // version string pattern, remove it from the string. |
| if strings.Contains(path, "{") { |
| thisSet[re.ReplaceAllString(path, "-$1.$2.$3")] = true |
| } |
| |
| for k := range thisSet { |
| set[k] = true |
| } |
| // Go lib dependencies are all linked into the build system |
| // using targets that are defined in the "golibs/BUILD.gn" file. |
| // We want to add the actual go library subdirectory as a dependency, |
| // but there is no easy way to determine that from the build target name. |
| if strings.Contains(path, "third_party/golibs") { |
| for k := range thisSet { |
| set[strings.ReplaceAll(k, ":", "/vendor/")] = true |
| } |
| } |
| |
| // Rust crate dependencies are all linked into the build system |
| // using targets that are defined in the "rust_crates/BUILD.gn" file. |
| // We want to add the actual rust_crate subdirectory as a dependency, |
| // but there is no easy way to determine that from the build target name. |
| // |
| // This adds all possible directories to the list. There is no harm if |
| // a given directory doesn't actually exist -- check-licenses will ignore |
| // those entries. |
| if strings.Contains(path, "rust_crates") { |
| for k := range thisSet { |
| k = re.ReplaceAllString(k, "$1-$2.$3.$4") |
| set[strings.ReplaceAll(k, ":", "/vendor/")] = true |
| set[strings.ReplaceAll(k, ":", "/ask2patch/")] = true |
| set[strings.ReplaceAll(k, ":", "/compat/")] = true |
| set[strings.ReplaceAll(k, ":", "/empty/")] = true |
| set[strings.ReplaceAll(k, ":", "/forks/")] = true |
| set[strings.ReplaceAll(k, ":", "/mirrors/")] = true |
| set[strings.ReplaceAll(k, ":", "/src/")] = true |
| } |
| } |
| } |
| |
| // Sort the results, so the outputs are deterministic. |
| results := make([]string, 0) |
| for k := range set { |
| results = append(results, k) |
| } |
| sort.Strings(results) |
| |
| return results, nil |
| } |