blob: 5ce45ce1263da553295a4a1e59f9988183296977 [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 file
import (
"bytes"
"crypto/sha1"
"encoding/base64"
"go.fuchsia.dev/fuchsia/tools/check-licenses/file/notice"
)
// FileData holds the text information (and some metadata) for a given file.
//
// Many NOTICE files will include several license texts in it.
// FileData represents one of those segments. It also maintains a line number
// that points to the location of this license text in the original NOTICE file,
// making it easier to find this license text again later.
type FileData struct {
FilePath string
LibraryName string
LineNumber int
Data []byte
LicenseType string
hash string
}
// Order implements sort.Interface for []*FileData based on the FilePath field.
type OrderFileData []*FileData
func (a OrderFileData) Len() int { return len(a) }
func (a OrderFileData) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a OrderFileData) Less(i, j int) bool {
if a[i].FilePath < a[j].FilePath {
return true
}
if a[i].FilePath > a[j].FilePath {
return false
}
return a[i].LineNumber < a[j].LineNumber
}
func NewFileData(path string, content []byte, filetype FileType) ([]*FileData, error) {
data := make([]*FileData, 0)
// The "LicenseFormat" field of each file is set at the project level
// (in README.fuchsia files) and it affects how they are analyzed here.
switch filetype {
// Default: File.LicenseFormat == Any
// This is most likely a regular source file in the repository.
// May or may not have copyright information.
case Any:
data = append(data, &FileData{
FilePath: path,
LineNumber: 0,
Data: bytes.TrimSpace(content),
})
// File.LicenseFormat == CopyrightHeader
// All source files belonging to "The Fuchsia Authors" (fuchsia.git)
// must contain Copyright header information.
case CopyrightHeader:
data = append(data, &FileData{
FilePath: path,
LineNumber: 0,
Data: bytes.TrimSpace(content),
})
// File.LicenseFormat == SingleLicense
// Regular LICENSE files that contain text for a single license.
case SingleLicense:
data = append(data, &FileData{
FilePath: path,
LineNumber: 0,
Data: bytes.TrimSpace(content),
})
// File.LicenseFormat == MultiLicense*
// NOTICE files that contain text for multiple licenses.
// See the files in the /notice subdirectory for more info.
case MultiLicenseChromium:
ndata, err := notice.ParseChromium(path, content)
if err != nil {
return nil, err
}
for _, d := range ndata {
data = append(data, &FileData{
FilePath: path,
LineNumber: d.LineNumber,
LibraryName: d.LibraryName,
Data: bytes.TrimSpace(d.LicenseText),
})
}
case MultiLicenseFlutter:
ndata, err := notice.ParseFlutter(path, content)
if err != nil {
return nil, err
}
for _, d := range ndata {
data = append(data, &FileData{
FilePath: path,
LineNumber: d.LineNumber,
LibraryName: d.LibraryName,
Data: bytes.TrimSpace(d.LicenseText),
})
}
case MultiLicenseAndroid:
ndata, err := notice.ParseAndroid(path, content)
if err != nil {
return nil, err
}
for _, d := range ndata {
data = append(data, &FileData{
FilePath: path,
LineNumber: d.LineNumber,
LibraryName: d.LibraryName,
Data: bytes.TrimSpace(d.LicenseText),
})
}
case MultiLicenseGoogle:
ndata, err := notice.ParseGoogle(path, content)
if err != nil {
return nil, err
}
for _, d := range ndata {
data = append(data, &FileData{
FilePath: path,
LineNumber: d.LineNumber,
LibraryName: d.LibraryName,
Data: bytes.TrimSpace(d.LicenseText),
})
}
}
for _, d := range data {
for _, r := range Config.Replacements {
d.Data = bytes.ReplaceAll(d.Data, []byte(r.Replace), []byte(r.With))
}
}
return data, nil
}
func (fd *FileData) SetData(data []byte) {
fd.Data = data
fd.hash = ""
fd.Hash()
}
// Hash the content of this filedata object, to help detect duplicate texts
// and help reduce the final NOTICE filesize.
func (fd *FileData) Hash() string {
if len(fd.hash) > 0 {
return fd.hash
}
hasher := sha1.New()
hasher.Write(bytes.TrimSpace(fd.Data))
fd.hash = base64.URLEncoding.EncodeToString(hasher.Sum(nil))
return fd.hash
}