build_license_metadata.go 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. // Copyright 2021 Google Inc. All rights reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package main
  15. import (
  16. "flag"
  17. "fmt"
  18. "io/ioutil"
  19. "os"
  20. "path/filepath"
  21. "strings"
  22. "google.golang.org/protobuf/encoding/prototext"
  23. "google.golang.org/protobuf/proto"
  24. "android/soong/compliance/license_metadata_proto"
  25. "android/soong/response"
  26. )
  27. func newMultiString(flags *flag.FlagSet, name, usage string) *multiString {
  28. var f multiString
  29. flags.Var(&f, name, usage)
  30. return &f
  31. }
  32. type multiString []string
  33. func (ms *multiString) String() string { return strings.Join(*ms, ", ") }
  34. func (ms *multiString) Set(s string) error { *ms = append(*ms, s); return nil }
  35. func main() {
  36. var expandedArgs []string
  37. for _, arg := range os.Args[1:] {
  38. if strings.HasPrefix(arg, "@") {
  39. f, err := os.Open(strings.TrimPrefix(arg, "@"))
  40. if err != nil {
  41. fmt.Fprintln(os.Stderr, err.Error())
  42. os.Exit(1)
  43. }
  44. respArgs, err := response.ReadRspFile(f)
  45. f.Close()
  46. if err != nil {
  47. fmt.Fprintln(os.Stderr, err.Error())
  48. os.Exit(1)
  49. }
  50. expandedArgs = append(expandedArgs, respArgs...)
  51. } else {
  52. expandedArgs = append(expandedArgs, arg)
  53. }
  54. }
  55. flags := flag.NewFlagSet("flags", flag.ExitOnError)
  56. packageName := flags.String("p", "", "license package name")
  57. moduleType := newMultiString(flags, "mt", "module type")
  58. moduleName := flags.String("mn", "", "module name")
  59. kinds := newMultiString(flags, "k", "license kinds")
  60. moduleClass := newMultiString(flags, "mc", "module class")
  61. conditions := newMultiString(flags, "c", "license conditions")
  62. notices := newMultiString(flags, "n", "license notice file")
  63. deps := newMultiString(flags, "d", "license metadata file dependency")
  64. sources := newMultiString(flags, "s", "source (input) dependency")
  65. built := newMultiString(flags, "t", "built targets")
  66. installed := newMultiString(flags, "i", "installed targets")
  67. roots := newMultiString(flags, "r", "root directory of project")
  68. installedMap := newMultiString(flags, "m", "map dependent targets to their installed names")
  69. isContainer := flags.Bool("is_container", false, "preserved dependent target name when given")
  70. outFile := flags.String("o", "", "output file")
  71. flags.Parse(expandedArgs)
  72. metadata := license_metadata_proto.LicenseMetadata{}
  73. metadata.PackageName = proto.String(*packageName)
  74. metadata.ModuleName = proto.String(*moduleName)
  75. metadata.ModuleTypes = *moduleType
  76. metadata.ModuleClasses = *moduleClass
  77. metadata.IsContainer = proto.Bool(*isContainer)
  78. metadata.Projects = findGitRoots(*roots)
  79. metadata.LicenseKinds = *kinds
  80. metadata.LicenseConditions = *conditions
  81. metadata.LicenseTexts = *notices
  82. metadata.Built = *built
  83. metadata.Installed = *installed
  84. metadata.InstallMap = convertInstalledMap(*installedMap)
  85. metadata.Sources = *sources
  86. metadata.Deps = convertDependencies(*deps)
  87. err := writeMetadata(*outFile, &metadata)
  88. if err != nil {
  89. fmt.Fprintf(os.Stderr, "error: %s\n", err.Error())
  90. os.Exit(2)
  91. }
  92. }
  93. func findGitRoots(dirs []string) []string {
  94. ret := make([]string, len(dirs))
  95. for i, dir := range dirs {
  96. ret[i] = findGitRoot(dir)
  97. }
  98. return ret
  99. }
  100. // findGitRoot finds the directory at or above dir that contains a ".git" directory. This isn't
  101. // guaranteed to exist, for example during remote execution, when sandboxed, when building from
  102. // infrastructure that doesn't use git, or when the .git directory has been removed to save space,
  103. // but it should be good enough for local builds. If no .git directory is found the original value
  104. // is returned.
  105. func findGitRoot(dir string) string {
  106. orig := dir
  107. for dir != "" && dir != "." && dir != "/" {
  108. _, err := os.Stat(filepath.Join(dir, ".git"))
  109. if err == nil {
  110. // Found dir/.git, return dir.
  111. return dir
  112. } else if !os.IsNotExist(err) {
  113. // Error finding .git, return original input.
  114. return orig
  115. }
  116. dir, _ = filepath.Split(dir)
  117. dir = strings.TrimSuffix(dir, "/")
  118. }
  119. return orig
  120. }
  121. // convertInstalledMap converts a list of colon-separated from:to pairs into InstallMap proto
  122. // messages.
  123. func convertInstalledMap(installMaps []string) []*license_metadata_proto.InstallMap {
  124. var ret []*license_metadata_proto.InstallMap
  125. for _, installMap := range installMaps {
  126. components := strings.Split(installMap, ":")
  127. if len(components) != 2 {
  128. panic(fmt.Errorf("install map entry %q contains %d colons, expected 1", installMap, len(components)-1))
  129. }
  130. ret = append(ret, &license_metadata_proto.InstallMap{
  131. FromPath: proto.String(components[0]),
  132. ContainerPath: proto.String(components[1]),
  133. })
  134. }
  135. return ret
  136. }
  137. // convertDependencies converts a colon-separated tuple of dependency:annotation:annotation...
  138. // into AnnotatedDependency proto messages.
  139. func convertDependencies(deps []string) []*license_metadata_proto.AnnotatedDependency {
  140. var ret []*license_metadata_proto.AnnotatedDependency
  141. for _, d := range deps {
  142. components := strings.Split(d, ":")
  143. dep := components[0]
  144. components = components[1:]
  145. ad := &license_metadata_proto.AnnotatedDependency{
  146. File: proto.String(dep),
  147. Annotations: make([]string, 0, len(components)),
  148. }
  149. for _, ann := range components {
  150. if len(ann) == 0 {
  151. continue
  152. }
  153. ad.Annotations = append(ad.Annotations, ann)
  154. }
  155. ret = append(ret, ad)
  156. }
  157. return ret
  158. }
  159. func writeMetadata(file string, metadata *license_metadata_proto.LicenseMetadata) error {
  160. buf, err := prototext.MarshalOptions{Multiline: true}.Marshal(metadata)
  161. if err != nil {
  162. return fmt.Errorf("error marshalling textproto: %w", err)
  163. }
  164. if file != "" {
  165. err = ioutil.WriteFile(file, buf, 0666)
  166. if err != nil {
  167. return fmt.Errorf("error writing textproto %q: %w", file, err)
  168. }
  169. } else {
  170. _, _ = os.Stdout.Write(buf)
  171. }
  172. return nil
  173. }