| // Copyright 2020 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 checklicenses |
| |
| import ( |
| "encoding/json" |
| "errors" |
| "fmt" |
| "io/ioutil" |
| "log" |
| "os" |
| "regexp" |
| "strings" |
| |
| "go.fuchsia.dev/fuchsia/tools/check-licenses/file" |
| "go.fuchsia.dev/fuchsia/tools/check-licenses/filetree" |
| "go.fuchsia.dev/fuchsia/tools/check-licenses/license" |
| "go.fuchsia.dev/fuchsia/tools/check-licenses/project" |
| "go.fuchsia.dev/fuchsia/tools/check-licenses/result" |
| "go.fuchsia.dev/fuchsia/tools/check-licenses/result/world" |
| ) |
| |
| var ( |
| ConfigVars map[string]string |
| ) |
| |
| func init() { |
| ConfigVars = make(map[string]string) |
| } |
| |
| type Include struct { |
| Path []string `json:"paths"` |
| Notes []string `json:"notes"` |
| Required bool `json:"required"` |
| } |
| |
| type CheckLicensesConfig struct { |
| LogLevel int `json:"logLevel"` |
| OutDir string `json:"outDir"` |
| |
| Includes []Include `json:"includes"` |
| |
| File *file.FileConfig `json:"file"` |
| License *license.LicenseConfig `json:"license"` |
| Project *project.ProjectConfig `json:"project"` |
| FileTree *filetree.FileTreeConfig `json:"filetree"` |
| Result *result.ResultConfig `json:"result"` |
| World *world.WorldConfig `json:"world"` |
| |
| Target string `json:"target"` |
| } |
| |
| func NewCheckLicensesConfig(path string) (*CheckLicensesConfig, error) { |
| b, err := ioutil.ReadFile(path) |
| if err != nil { |
| return nil, fmt.Errorf("Failed to read config file [%v]: %w\n", path, err) |
| } |
| |
| c, err := NewCheckLicensesConfigJson(string(b)) |
| if err != nil { |
| return nil, fmt.Errorf("Failed to parse JSON config file [%v]: %v\n", path, err) |
| } |
| return c, nil |
| } |
| |
| func NewCheckLicensesConfigJson(configJson string) (*CheckLicensesConfig, error) { |
| for k, v := range ConfigVars { |
| configJson = strings.ReplaceAll(configJson, k, v) |
| } |
| |
| // Make sure all variables have been replaced. |
| r := regexp.MustCompile(`({[\w]+})`) |
| matches := r.FindAllStringSubmatch(configJson, -1) |
| |
| if len(matches) > 0 { |
| return nil, fmt.Errorf("Found unexpanded variable(s) in config file: %v\n", configJson) |
| } |
| |
| c := &CheckLicensesConfig{ |
| File: file.NewConfig(), |
| License: license.NewConfig(), |
| Project: project.NewConfig(), |
| FileTree: filetree.NewConfig(), |
| Result: result.NewConfig(), |
| World: world.NewConfig(), |
| } |
| |
| d := json.NewDecoder(strings.NewReader(configJson)) |
| // TODO: Uncomment once "Filter" fields are removed from all config files. |
| //d.DisallowUnknownFields() |
| if err := d.Decode(c); err != nil { |
| return nil, err |
| } |
| |
| // Loop over the Includes field and merge in all config files |
| // from that list, recursively. |
| if len(c.Includes) > 0 { |
| for _, include := range c.Includes { |
| for _, path := range include.Path { |
| c2, err := NewCheckLicensesConfig(path) |
| // If we get an error loading the config file, |
| // it may be because a given submodule isn't |
| // available on your machine (e.g. //vendor/google). |
| // |
| // Only error out if this config section is marked |
| // as "required". |
| if err != nil { |
| if errors.Is(err, os.ErrNotExist) && !include.Required { |
| if c.LogLevel > 0 { |
| log.Printf("Failed to create config file for %s: %v.\n", path, err) |
| } |
| continue |
| } else { |
| return nil, err |
| } |
| } |
| c.Merge(c2) |
| } |
| } |
| } |
| |
| return c, nil |
| } |
| |
| func (c *CheckLicensesConfig) Merge(other *CheckLicensesConfig) error { |
| c.File.Merge(other.File) |
| c.License.Merge(other.License) |
| c.FileTree.Merge(other.FileTree) |
| c.Project.Merge(other.Project) |
| c.Result.Merge(other.Result) |
| c.World.Merge(other.World) |
| |
| c.Includes = append(c.Includes, other.Includes...) |
| if c.OutDir == "" { |
| c.OutDir = other.OutDir |
| } |
| if other.LogLevel > c.LogLevel { |
| c.LogLevel = other.LogLevel |
| } |
| |
| return nil |
| } |