blob: 5cdd3020f6339cd6b31c9326567618cd2f8c07c5 [file] [log] [blame]
Wei Lia1aa2972024-06-21 13:08:51 -07001// Copyright 2024 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
15package android
16
17import (
18 "bytes"
19 "encoding/csv"
20 "fmt"
21 "slices"
22 "strconv"
23 "strings"
24
25 "github.com/google/blueprint"
26)
27
28var (
29 // Constants of property names used in compliance metadata of modules
30 ComplianceMetadataProp = struct {
31 NAME string
32 PACKAGE string
33 MODULE_TYPE string
34 OS string
35 ARCH string
36 IS_PRIMARY_ARCH string
37 VARIANT string
38 IS_STATIC_LIB string
39 INSTALLED_FILES string
40 BUILT_FILES string
41 STATIC_DEPS string
42 STATIC_DEP_FILES string
43 WHOLE_STATIC_DEPS string
44 WHOLE_STATIC_DEP_FILES string
45 LICENSES string
46
47 // module_type=package
48 PKG_DEFAULT_APPLICABLE_LICENSES string
49
50 // module_type=license
51 LIC_LICENSE_KINDS string
52 LIC_LICENSE_TEXT string
53 LIC_PACKAGE_NAME string
54
55 // module_type=license_kind
56 LK_CONDITIONS string
57 LK_URL string
58 }{
59 "name",
60 "package",
61 "module_type",
62 "os",
63 "arch",
64 "is_primary_arch",
65 "variant",
66 "is_static_lib",
67 "installed_files",
68 "built_files",
69 "static_deps",
70 "static_dep_files",
71 "whole_static_deps",
72 "whole_static_dep_files",
73 "licenses",
74
75 "pkg_default_applicable_licenses",
76
77 "lic_license_kinds",
78 "lic_license_text",
79 "lic_package_name",
80
81 "lk_conditions",
82 "lk_url",
83 }
84
85 // A constant list of all property names in compliance metadata
86 // Order of properties here is the order of columns in the exported CSV file.
87 COMPLIANCE_METADATA_PROPS = []string{
88 ComplianceMetadataProp.NAME,
89 ComplianceMetadataProp.PACKAGE,
90 ComplianceMetadataProp.MODULE_TYPE,
91 ComplianceMetadataProp.OS,
92 ComplianceMetadataProp.ARCH,
93 ComplianceMetadataProp.VARIANT,
94 ComplianceMetadataProp.IS_STATIC_LIB,
95 ComplianceMetadataProp.IS_PRIMARY_ARCH,
96 // Space separated installed files
97 ComplianceMetadataProp.INSTALLED_FILES,
98 // Space separated built files
99 ComplianceMetadataProp.BUILT_FILES,
100 // Space separated module names of static dependencies
101 ComplianceMetadataProp.STATIC_DEPS,
102 // Space separated file paths of static dependencies
103 ComplianceMetadataProp.STATIC_DEP_FILES,
104 // Space separated module names of whole static dependencies
105 ComplianceMetadataProp.WHOLE_STATIC_DEPS,
106 // Space separated file paths of whole static dependencies
107 ComplianceMetadataProp.WHOLE_STATIC_DEP_FILES,
108 ComplianceMetadataProp.LICENSES,
109 // module_type=package
110 ComplianceMetadataProp.PKG_DEFAULT_APPLICABLE_LICENSES,
111 // module_type=license
112 ComplianceMetadataProp.LIC_LICENSE_KINDS,
113 ComplianceMetadataProp.LIC_LICENSE_TEXT, // resolve to file paths
114 ComplianceMetadataProp.LIC_PACKAGE_NAME,
115 // module_type=license_kind
116 ComplianceMetadataProp.LK_CONDITIONS,
117 ComplianceMetadataProp.LK_URL,
118 }
119)
120
121// ComplianceMetadataInfo provides all metadata of a module, e.g. name, module type, package, license,
122// dependencies, built/installed files, etc. It is a wrapper on a map[string]string with some utility
123// methods to get/set properties' values.
124type ComplianceMetadataInfo struct {
125 properties map[string]string
126}
127
128func NewComplianceMetadataInfo() *ComplianceMetadataInfo {
129 return &ComplianceMetadataInfo{
130 properties: map[string]string{},
131 }
132}
133
134func (c *ComplianceMetadataInfo) SetStringValue(propertyName string, value string) {
135 if !slices.Contains(COMPLIANCE_METADATA_PROPS, propertyName) {
136 panic(fmt.Errorf("Unknown metadata property: %s.", propertyName))
137 }
138 c.properties[propertyName] = value
139}
140
141func (c *ComplianceMetadataInfo) SetListValue(propertyName string, value []string) {
142 c.SetStringValue(propertyName, strings.TrimSpace(strings.Join(value, " ")))
143}
144
145func (c *ComplianceMetadataInfo) getStringValue(propertyName string) string {
146 if !slices.Contains(COMPLIANCE_METADATA_PROPS, propertyName) {
147 panic(fmt.Errorf("Unknown metadata property: %s.", propertyName))
148 }
149 return c.properties[propertyName]
150}
151
152func (c *ComplianceMetadataInfo) getAllValues() map[string]string {
153 return c.properties
154}
155
156var (
157 ComplianceMetadataProvider = blueprint.NewProvider[*ComplianceMetadataInfo]()
158)
159
160// buildComplianceMetadataProvider starts with the ModuleContext.ComplianceMetadataInfo() and fills in more common metadata
161// for different module types without accessing their private fields but through android.Module interface
162// and public/private fields of package android. The final metadata is stored to a module's ComplianceMetadataProvider.
Yu Liuddc28332024-08-09 22:48:30 +0000163func buildComplianceMetadataProvider(ctx *moduleContext, m *ModuleBase) {
Wei Lia1aa2972024-06-21 13:08:51 -0700164 complianceMetadataInfo := ctx.ComplianceMetadataInfo()
165 complianceMetadataInfo.SetStringValue(ComplianceMetadataProp.NAME, m.Name())
166 complianceMetadataInfo.SetStringValue(ComplianceMetadataProp.PACKAGE, ctx.ModuleDir())
167 complianceMetadataInfo.SetStringValue(ComplianceMetadataProp.MODULE_TYPE, ctx.ModuleType())
168
169 switch ctx.ModuleType() {
170 case "license":
171 licenseModule := m.module.(*licenseModule)
172 complianceMetadataInfo.SetListValue(ComplianceMetadataProp.LIC_LICENSE_KINDS, licenseModule.properties.License_kinds)
173 complianceMetadataInfo.SetListValue(ComplianceMetadataProp.LIC_LICENSE_TEXT, PathsForModuleSrc(ctx, licenseModule.properties.License_text).Strings())
174 complianceMetadataInfo.SetStringValue(ComplianceMetadataProp.LIC_PACKAGE_NAME, String(licenseModule.properties.Package_name))
175 case "license_kind":
176 licenseKindModule := m.module.(*licenseKindModule)
177 complianceMetadataInfo.SetListValue(ComplianceMetadataProp.LK_CONDITIONS, licenseKindModule.properties.Conditions)
178 complianceMetadataInfo.SetStringValue(ComplianceMetadataProp.LK_URL, licenseKindModule.properties.Url)
179 default:
180 complianceMetadataInfo.SetStringValue(ComplianceMetadataProp.OS, ctx.Os().String())
181 complianceMetadataInfo.SetStringValue(ComplianceMetadataProp.ARCH, ctx.Arch().String())
182 complianceMetadataInfo.SetStringValue(ComplianceMetadataProp.IS_PRIMARY_ARCH, strconv.FormatBool(ctx.PrimaryArch()))
183 complianceMetadataInfo.SetStringValue(ComplianceMetadataProp.VARIANT, ctx.ModuleSubDir())
184 if m.primaryLicensesProperty != nil && m.primaryLicensesProperty.getName() == "licenses" {
185 complianceMetadataInfo.SetListValue(ComplianceMetadataProp.LICENSES, m.primaryLicensesProperty.getStrings())
186 }
187
188 var installed InstallPaths
Yu Liuddc28332024-08-09 22:48:30 +0000189 installed = append(installed, ctx.installFiles...)
Wei Lia1aa2972024-06-21 13:08:51 -0700190 installed = append(installed, m.katiInstalls.InstallPaths()...)
191 installed = append(installed, m.katiSymlinks.InstallPaths()...)
192 installed = append(installed, m.katiInitRcInstalls.InstallPaths()...)
193 installed = append(installed, m.katiVintfInstalls.InstallPaths()...)
194 complianceMetadataInfo.SetListValue(ComplianceMetadataProp.INSTALLED_FILES, FirstUniqueStrings(installed.Strings()))
195 }
196 ctx.setProvider(ComplianceMetadataProvider, complianceMetadataInfo)
197}
198
199func init() {
200 RegisterComplianceMetadataSingleton(InitRegistrationContext)
201}
202
203func RegisterComplianceMetadataSingleton(ctx RegistrationContext) {
204 ctx.RegisterParallelSingletonType("compliance_metadata_singleton", complianceMetadataSingletonFactory)
205}
206
207var (
208 // sqlite3 command line tool
209 sqlite3 = pctx.HostBinToolVariable("sqlite3", "sqlite3")
210
211 // Command to import .csv files to sqlite3 database
212 importCsv = pctx.AndroidStaticRule("importCsv",
213 blueprint.RuleParams{
214 Command: `rm -rf $out && ` +
215 `${sqlite3} $out ".import --csv $in modules" && ` +
216 `${sqlite3} $out ".import --csv ${make_metadata} make_metadata" && ` +
217 `${sqlite3} $out ".import --csv ${make_modules} make_modules"`,
218 CommandDeps: []string{"${sqlite3}"},
219 }, "make_metadata", "make_modules")
220)
221
222func complianceMetadataSingletonFactory() Singleton {
223 return &complianceMetadataSingleton{}
224}
225
226type complianceMetadataSingleton struct {
227}
228
229func writerToCsv(csvWriter *csv.Writer, row []string) {
230 err := csvWriter.Write(row)
231 if err != nil {
232 panic(err)
233 }
234}
235
236// Collect compliance metadata from all Soong modules, write to a CSV file and
237// import compliance metadata from Make and Soong to a sqlite3 database.
238func (c *complianceMetadataSingleton) GenerateBuildActions(ctx SingletonContext) {
239 if !ctx.Config().HasDeviceProduct() {
240 return
241 }
242 var buffer bytes.Buffer
243 csvWriter := csv.NewWriter(&buffer)
244
245 // Collect compliance metadata of modules in Soong and write to out/soong/compliance-metadata/<product>/soong-modules.csv file.
246 columnNames := []string{"id"}
247 columnNames = append(columnNames, COMPLIANCE_METADATA_PROPS...)
248 writerToCsv(csvWriter, columnNames)
249
250 rowId := -1
251 ctx.VisitAllModules(func(module Module) {
252 if !module.Enabled(ctx) {
253 return
254 }
255 moduleType := ctx.ModuleType(module)
256 if moduleType == "package" {
257 metadataMap := map[string]string{
258 ComplianceMetadataProp.NAME: ctx.ModuleName(module),
259 ComplianceMetadataProp.MODULE_TYPE: ctx.ModuleType(module),
260 ComplianceMetadataProp.PKG_DEFAULT_APPLICABLE_LICENSES: strings.Join(module.base().primaryLicensesProperty.getStrings(), " "),
261 }
262 rowId = rowId + 1
263 metadata := []string{strconv.Itoa(rowId)}
264 for _, propertyName := range COMPLIANCE_METADATA_PROPS {
265 metadata = append(metadata, metadataMap[propertyName])
266 }
267 writerToCsv(csvWriter, metadata)
268 return
269 }
Yu Liu663e4502024-08-12 18:23:59 +0000270 if provider, ok := ctx.otherModuleProvider(module, ComplianceMetadataProvider); ok {
Wei Lia1aa2972024-06-21 13:08:51 -0700271 metadataInfo := provider.(*ComplianceMetadataInfo)
272 rowId = rowId + 1
273 metadata := []string{strconv.Itoa(rowId)}
274 for _, propertyName := range COMPLIANCE_METADATA_PROPS {
275 metadata = append(metadata, metadataInfo.getStringValue(propertyName))
276 }
277 writerToCsv(csvWriter, metadata)
278 return
279 }
280 })
281 csvWriter.Flush()
282
283 deviceProduct := ctx.Config().DeviceProduct()
284 modulesCsv := PathForOutput(ctx, "compliance-metadata", deviceProduct, "soong-modules.csv")
285 WriteFileRuleVerbatim(ctx, modulesCsv, buffer.String())
286
287 // Metadata generated in Make
288 makeMetadataCsv := PathForOutput(ctx, "compliance-metadata", deviceProduct, "make-metadata.csv")
289 makeModulesCsv := PathForOutput(ctx, "compliance-metadata", deviceProduct, "make-modules.csv")
290
291 // Import metadata from Make and Soong to sqlite3 database
292 complianceMetadataDb := PathForOutput(ctx, "compliance-metadata", deviceProduct, "compliance-metadata.db")
293 ctx.Build(pctx, BuildParams{
294 Rule: importCsv,
295 Input: modulesCsv,
296 Implicits: []Path{
297 makeMetadataCsv,
298 makeModulesCsv,
299 },
300 Output: complianceMetadataDb,
301 Args: map[string]string{
302 "make_metadata": makeMetadataCsv.String(),
303 "make_modules": makeModulesCsv.String(),
304 },
305 })
306
307 // Phony rule "compliance-metadata.db". "m compliance-metadata.db" to create the compliance metadata database.
308 ctx.Build(pctx, BuildParams{
309 Rule: blueprint.Phony,
310 Inputs: []Path{complianceMetadataDb},
311 Output: PathForPhony(ctx, "compliance-metadata.db"),
312 })
313
314}