blob: 4a5aa94224b057ac36199d7db604097c73df2c1e [file] [log] [blame]
// 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 world
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"sort"
"strings"
"go.fuchsia.dev/fuchsia/tools/check-licenses/file"
"go.fuchsia.dev/fuchsia/tools/check-licenses/project"
)
func (w *World) FilterProjects() error {
projects := make([]*project.Project, 0)
projectsMap := make(map[*project.Project]bool, 0)
filepaths := make([]string, 0)
var err error
if isDir(Config.Target) {
w.Status.WriteString(fmt.Sprintf("Filtering projects using a folder prefix check: `%s`\n", Config.Target))
filepaths = w.FilterProjectsUsingDirectoryPrefix(filepaths)
}
filepaths, err = w.FilterExtraProjects(filepaths)
if err != nil {
return err
}
fileMap, err := w.getFileMap()
if err != nil {
return err
}
// Loop through the content, cleaning up the path strings
// so we can map them to entries in our project map.
for _, fp := range filepaths {
if p, ok := fileMap[fp]; ok {
projectsMap[p] = true
}
}
for p := range projectsMap {
projects = append(projects, p)
}
sort.Sort(project.Order(projects))
w.FilteredProjects = projects
filteredCount := len(w.FilteredProjects)
totalCount := len(w.Projects)
filteredOutCount := totalCount - filteredCount
w.Status.WriteString(fmt.Sprintf("%v projects remain (filtered out %v from the full list of %v projects).\n", filteredCount, filteredOutCount, totalCount))
return nil
}
func (w *World) FilterProjectsUsingDirectoryPrefix(filepaths []string) []string {
for _, p := range w.Projects {
for _, f := range p.Files {
if strings.HasPrefix(f.Path, Config.Target) {
filepaths = append(filepaths, f.Path)
}
}
}
return filepaths
}
func (w *World) FilterExtraProjects(filepaths []string) ([]string, error) {
for _, projectFile := range Config.Filters {
w.Status.WriteString(fmt.Sprintf("Adding additional projects from json file: `%s`\n", projectFile))
f, err := os.Open(projectFile)
if err != nil {
return filepaths, err
}
defer f.Close()
content, err := ioutil.ReadAll(f)
if err != nil {
return filepaths, err
}
projects := make([]string, 0)
if err = json.Unmarshal(content, &projects); err != nil {
return filepaths, fmt.Errorf("Failed to unmarshal project json file [%v]: %v\n", projectFile, err)
}
for _, p := range projects {
filepaths = append(filepaths, p)
}
}
return filepaths, nil
}
func (w *World) getFileMap() (map[string]*project.Project, error) {
// Create a mapping that goes from file path to project,
// so we can retrieve the projects that match dependencies in the
// gn gen file.
fileMap := make(map[string]*project.Project, 0)
for _, p := range project.AllProjects {
allFiles := make([]*file.File, 0)
allFiles = append(allFiles, p.Files...)
allFiles = append(allFiles, p.LicenseFile...)
for _, f := range allFiles {
path := f.Path
if strings.Contains(f.Path, Config.FuchsiaDir) {
var err error
path, err = filepath.Rel(Config.FuchsiaDir, f.Path)
if err != nil {
return nil, err
}
}
filePath := "//" + path
folderPath := "//" + filepath.Dir(path)
// "gn gen" may reveal that the current workspace has a a dependency on a LICENSE file.
// That LICENSE file may be used in two or more different projects across fuchsia.git.
// There's no way for us to tell which project actually contributes to the build.
//
// We want to deterministically generate the final NOTICE file, so in this situation
// we simply choose the project that comes first alphabetically.
//
// In practice this simple strategy should be OK. "gn desc" / "gn gen" will undoubtedly
// also have dependencies on other files in the project, which will ensure that the correct
// project is included (even if we also occasionally include an unrelated one).
if otherP, ok := fileMap[filePath]; ok {
if p.Root < otherP.Root {
fileMap[filePath] = p
fileMap[folderPath] = p
}
} else {
fileMap[filePath] = p
fileMap[folderPath] = p
}
}
}
return fileMap, nil
}
func isDir(str string) bool {
_, err := os.Stat(str)
if err == nil {
return true
}
if os.IsNotExist(err) {
return false
}
return false
}