blob: a9dd1ff1d661983dae534b612b1f49b45795ceb9 [file] [log] [blame]
// 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 main
import (
"context"
"encoding/json"
"flag"
"fmt"
"io/ioutil"
"log"
"os"
"runtime/pprof"
"runtime/trace"
"strings"
checklicenses "go.fuchsia.dev/fuchsia/tools/check-licenses"
)
var (
configFile = flag.String("config_file", "tools/check-licenses/config/config.json", "Comma separated list of paths to json files.")
pproffile = flag.String("pprof", "", "generate file that can be parsed by go tool pprof")
tracefile = flag.String("trace", "", "generate file that can be parsed by go tool trace")
skipDirs = flag.String("skip_dirs", "", "Comma separated list of directory names to skip when traversing a directory tree. This arg is added to the list of skipdirs in the config file.")
skipFiles = flag.String("skip_files", "", "Comma separated list of file names to skip when traversing a directory tree. This arg is added to the list of skipfiles in the config file.")
prohibitedLicenseTypes = flag.String("prohibited_license_types", "", "Comma separated list of license types that are prohibited. This arg is added to the list of prohibitedLicenseTypes in the config file.")
exitOnDirRestrictedLicense = flag.Bool("exit_on_dir_restricted_license", true, "If true, exits if it encounters a license used outside of its allowed directories.")
exitOnProhibitedLicenseTypes = flag.Bool("exit_on_prohibited_license_types", true, "If true, exits if it encounters a prohibited license type.")
exitOnUnlicensedFiles = flag.Bool("exit_on_unlicensed_files", true, "If true, exits if it encounters files that are unlicensed.")
licensePatternDir = flag.String("license_pattern_dir", "", "Location of directory containing pattern files for all licenses that may exist in the given code base.")
noticeTxtFiles = flag.String("notice_txt", "", "Comma separated list of NOTICE.txt files to parse, in addition to those listed in config.json.")
licenseAllowList = flag.String("license_allow_list", "", "Map of license pattern file (.lic) to list of directories where the license can be used.")
logLevel = flag.Int("log_level", 0, "Log level")
baseDir = flag.String("base_dir", "", "Root location to begin directory traversal.")
outDir = flag.String("out_dir", "", "Directory to write outputs to.")
outputLicenseFile = flag.Bool("output_license_file", true, "If true, outputs a license file with all the licenses for the project.")
target = flag.String("target", "", "Analyze the dependency tree of a specific GN build target.")
)
func mainImpl() error {
flag.Parse()
if *tracefile != "" {
f, err := os.Create(*tracefile)
if err != nil {
return fmt.Errorf("failed to create trace output file: %s", err)
}
defer f.Close()
if err := trace.Start(f); err != nil {
return fmt.Errorf("failed to start trace: %s", err)
}
defer trace.Stop()
}
if *pproffile != "" {
f, err := os.Create(*pproffile)
if err != nil {
return fmt.Errorf("failed to create pprof output file: %s", err)
}
defer f.Close()
if err := pprof.StartCPUProfile(f); err != nil {
return fmt.Errorf("failed to start pprof: %s", err)
}
defer pprof.StopCPUProfile()
}
// TODO(jcecil): incorporate https://godoc.org/github.com/golang/glog
if *logLevel == 0 {
log.SetOutput(ioutil.Discard)
}
config := &checklicenses.Config{}
if *configFile != "" {
split := strings.Split(*configFile, ",")
for _, path := range split {
if path != "" {
c, err := checklicenses.NewConfig(path)
if err != nil {
return fmt.Errorf("failed to initialize config %s: %s", path, err)
}
config.Merge(c)
}
}
}
if *skipDirs != "" {
split := strings.Split(*skipDirs, ",")
for _, s := range split {
if s != "" {
config.SkipDirs = append(config.SkipDirs, s)
}
}
}
config.SkipDirs = append(config.SkipDirs, additionalSkipDirs...)
if *skipFiles != "" {
split := strings.Split(*skipFiles, ",")
for _, s := range split {
if s != "" {
config.SkipFiles = append(config.SkipFiles, s)
}
}
}
// TODO(b/172070492): Remove this list once completed.
for _, f := range additionalSkipFiles {
config.SkipFiles = append(config.SkipFiles, strings.ToLower(f))
}
if *prohibitedLicenseTypes != "" {
split := strings.Split(*prohibitedLicenseTypes, ",")
for _, s := range split {
if s != "" {
config.ProhibitedLicenseTypes = append(config.ProhibitedLicenseTypes, s)
}
}
}
if *noticeTxtFiles != "" {
split := strings.Split(*noticeTxtFiles, ",")
for _, s := range split {
if s != "" {
config.NoticeTxtFiles = append(config.NoticeTxtFiles, s)
}
}
}
if *licenseAllowList != "" {
blob := []byte(*licenseAllowList)
result := make(map[string][]string)
err := json.Unmarshal(blob, &result)
if err != nil {
return fmt.Errorf("failed to initialize license allow list: %s", err)
}
for k, v := range result {
if _, ok := config.LicenseAllowList[k]; !ok {
config.LicenseAllowList[k] = []string{}
}
config.LicenseAllowList[k] = append(config.LicenseAllowList[k], v...)
}
}
// TODO(fxb/42986): Remove ExitOnProhibitedLicenseTypes and ExitOnUnlicensedFiles
// flags once fxb/42986 is completed.
config.ExitOnProhibitedLicenseTypes = *exitOnProhibitedLicenseTypes
config.ExitOnUnlicensedFiles = *exitOnUnlicensedFiles
config.ExitOnDirRestrictedLicense = *exitOnDirRestrictedLicense
config.OutputLicenseFile = *outputLicenseFile
if *licensePatternDir != "" {
if info, err := os.Stat(*licensePatternDir); os.IsNotExist(err) && info.IsDir() {
return fmt.Errorf("license pattern directory path %q does not exist!", *licensePatternDir)
}
config.LicensePatternDir = *licensePatternDir
}
if *baseDir != "" {
if info, err := os.Stat(*baseDir); os.IsNotExist(err) && info.IsDir() {
return fmt.Errorf("base directory path %q does not exist!", *baseDir)
}
config.BaseDir = *baseDir
}
if *outDir != "" {
if info, err := os.Stat(*outDir); os.IsNotExist(err) && info.IsDir() {
return fmt.Errorf("out directory path %q does not exist!", *baseDir)
}
config.OutDir = *outDir
}
if *target != "" {
log.Printf("WARNING: Flag \"target\" was set to \"%v\", but that flag is currently unsupported. check-licenses will parse the full directory tree.\n", *target)
//TODO: enable target-based dependency traversal
//config.Target = *target
}
if err := checklicenses.Run(context.Background(), config); err != nil {
return fmt.Errorf("failed to analyze the given directory: %v", err)
}
return nil
}
func main() {
if err := mainImpl(); err != nil {
fmt.Fprintf(os.Stderr, "check-licenses: %s\n", err)
os.Exit(1)
}
}