blob: d28831e62586e3a5ff773c3b789fab74f0327a2e [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
Yu Liu467d7c52024-09-18 21:54:44 +0000128type complianceMetadataInfoGob struct {
129 Properties map[string]string
130}
131
Wei Lia1aa2972024-06-21 13:08:51 -0700132func NewComplianceMetadataInfo() *ComplianceMetadataInfo {
133 return &ComplianceMetadataInfo{
134 properties: map[string]string{},
135 }
136}
137
Yu Liu467d7c52024-09-18 21:54:44 +0000138func (m *ComplianceMetadataInfo) ToGob() *complianceMetadataInfoGob {
139 return &complianceMetadataInfoGob{
140 Properties: m.properties,
Yu Liu26a716d2024-08-30 23:40:32 +0000141 }
Yu Liu467d7c52024-09-18 21:54:44 +0000142}
Yu Liu26a716d2024-08-30 23:40:32 +0000143
Yu Liu467d7c52024-09-18 21:54:44 +0000144func (m *ComplianceMetadataInfo) FromGob(data *complianceMetadataInfoGob) {
145 m.properties = data.Properties
146}
147
148func (c *ComplianceMetadataInfo) GobEncode() ([]byte, error) {
149 return blueprint.CustomGobEncode[complianceMetadataInfoGob](c)
Yu Liu26a716d2024-08-30 23:40:32 +0000150}
151
152func (c *ComplianceMetadataInfo) GobDecode(data []byte) error {
Yu Liu467d7c52024-09-18 21:54:44 +0000153 return blueprint.CustomGobDecode[complianceMetadataInfoGob](data, c)
Yu Liu26a716d2024-08-30 23:40:32 +0000154}
155
Wei Lia1aa2972024-06-21 13:08:51 -0700156func (c *ComplianceMetadataInfo) SetStringValue(propertyName string, value string) {
157 if !slices.Contains(COMPLIANCE_METADATA_PROPS, propertyName) {
158 panic(fmt.Errorf("Unknown metadata property: %s.", propertyName))
159 }
160 c.properties[propertyName] = value
161}
162
163func (c *ComplianceMetadataInfo) SetListValue(propertyName string, value []string) {
164 c.SetStringValue(propertyName, strings.TrimSpace(strings.Join(value, " ")))
165}
166
167func (c *ComplianceMetadataInfo) getStringValue(propertyName string) string {
168 if !slices.Contains(COMPLIANCE_METADATA_PROPS, propertyName) {
169 panic(fmt.Errorf("Unknown metadata property: %s.", propertyName))
170 }
171 return c.properties[propertyName]
172}
173
174func (c *ComplianceMetadataInfo) getAllValues() map[string]string {
175 return c.properties
176}
177
178var (
179 ComplianceMetadataProvider = blueprint.NewProvider[*ComplianceMetadataInfo]()
180)
181
182// buildComplianceMetadataProvider starts with the ModuleContext.ComplianceMetadataInfo() and fills in more common metadata
183// for different module types without accessing their private fields but through android.Module interface
184// and public/private fields of package android. The final metadata is stored to a module's ComplianceMetadataProvider.
Yu Liuddc28332024-08-09 22:48:30 +0000185func buildComplianceMetadataProvider(ctx *moduleContext, m *ModuleBase) {
Wei Lia1aa2972024-06-21 13:08:51 -0700186 complianceMetadataInfo := ctx.ComplianceMetadataInfo()
187 complianceMetadataInfo.SetStringValue(ComplianceMetadataProp.NAME, m.Name())
188 complianceMetadataInfo.SetStringValue(ComplianceMetadataProp.PACKAGE, ctx.ModuleDir())
189 complianceMetadataInfo.SetStringValue(ComplianceMetadataProp.MODULE_TYPE, ctx.ModuleType())
190
191 switch ctx.ModuleType() {
192 case "license":
193 licenseModule := m.module.(*licenseModule)
194 complianceMetadataInfo.SetListValue(ComplianceMetadataProp.LIC_LICENSE_KINDS, licenseModule.properties.License_kinds)
195 complianceMetadataInfo.SetListValue(ComplianceMetadataProp.LIC_LICENSE_TEXT, PathsForModuleSrc(ctx, licenseModule.properties.License_text).Strings())
196 complianceMetadataInfo.SetStringValue(ComplianceMetadataProp.LIC_PACKAGE_NAME, String(licenseModule.properties.Package_name))
197 case "license_kind":
198 licenseKindModule := m.module.(*licenseKindModule)
199 complianceMetadataInfo.SetListValue(ComplianceMetadataProp.LK_CONDITIONS, licenseKindModule.properties.Conditions)
200 complianceMetadataInfo.SetStringValue(ComplianceMetadataProp.LK_URL, licenseKindModule.properties.Url)
201 default:
202 complianceMetadataInfo.SetStringValue(ComplianceMetadataProp.OS, ctx.Os().String())
203 complianceMetadataInfo.SetStringValue(ComplianceMetadataProp.ARCH, ctx.Arch().String())
204 complianceMetadataInfo.SetStringValue(ComplianceMetadataProp.IS_PRIMARY_ARCH, strconv.FormatBool(ctx.PrimaryArch()))
205 complianceMetadataInfo.SetStringValue(ComplianceMetadataProp.VARIANT, ctx.ModuleSubDir())
206 if m.primaryLicensesProperty != nil && m.primaryLicensesProperty.getName() == "licenses" {
207 complianceMetadataInfo.SetListValue(ComplianceMetadataProp.LICENSES, m.primaryLicensesProperty.getStrings())
208 }
209
210 var installed InstallPaths
Yu Liuddc28332024-08-09 22:48:30 +0000211 installed = append(installed, ctx.installFiles...)
Yu Liud46e5ae2024-08-15 18:46:17 +0000212 installed = append(installed, ctx.katiInstalls.InstallPaths()...)
213 installed = append(installed, ctx.katiSymlinks.InstallPaths()...)
Yu Liu82a6d142024-08-27 19:02:29 +0000214 installed = append(installed, ctx.katiInitRcInstalls.InstallPaths()...)
215 installed = append(installed, ctx.katiVintfInstalls.InstallPaths()...)
Wei Lia1aa2972024-06-21 13:08:51 -0700216 complianceMetadataInfo.SetListValue(ComplianceMetadataProp.INSTALLED_FILES, FirstUniqueStrings(installed.Strings()))
217 }
218 ctx.setProvider(ComplianceMetadataProvider, complianceMetadataInfo)
219}
220
221func init() {
222 RegisterComplianceMetadataSingleton(InitRegistrationContext)
223}
224
225func RegisterComplianceMetadataSingleton(ctx RegistrationContext) {
226 ctx.RegisterParallelSingletonType("compliance_metadata_singleton", complianceMetadataSingletonFactory)
227}
228
229var (
230 // sqlite3 command line tool
231 sqlite3 = pctx.HostBinToolVariable("sqlite3", "sqlite3")
232
233 // Command to import .csv files to sqlite3 database
234 importCsv = pctx.AndroidStaticRule("importCsv",
235 blueprint.RuleParams{
236 Command: `rm -rf $out && ` +
237 `${sqlite3} $out ".import --csv $in modules" && ` +
238 `${sqlite3} $out ".import --csv ${make_metadata} make_metadata" && ` +
239 `${sqlite3} $out ".import --csv ${make_modules} make_modules"`,
240 CommandDeps: []string{"${sqlite3}"},
241 }, "make_metadata", "make_modules")
242)
243
244func complianceMetadataSingletonFactory() Singleton {
245 return &complianceMetadataSingleton{}
246}
247
248type complianceMetadataSingleton struct {
249}
250
251func writerToCsv(csvWriter *csv.Writer, row []string) {
252 err := csvWriter.Write(row)
253 if err != nil {
254 panic(err)
255 }
256}
257
258// Collect compliance metadata from all Soong modules, write to a CSV file and
259// import compliance metadata from Make and Soong to a sqlite3 database.
260func (c *complianceMetadataSingleton) GenerateBuildActions(ctx SingletonContext) {
261 if !ctx.Config().HasDeviceProduct() {
262 return
263 }
264 var buffer bytes.Buffer
265 csvWriter := csv.NewWriter(&buffer)
266
267 // Collect compliance metadata of modules in Soong and write to out/soong/compliance-metadata/<product>/soong-modules.csv file.
268 columnNames := []string{"id"}
269 columnNames = append(columnNames, COMPLIANCE_METADATA_PROPS...)
270 writerToCsv(csvWriter, columnNames)
271
272 rowId := -1
273 ctx.VisitAllModules(func(module Module) {
274 if !module.Enabled(ctx) {
275 return
276 }
277 moduleType := ctx.ModuleType(module)
278 if moduleType == "package" {
279 metadataMap := map[string]string{
280 ComplianceMetadataProp.NAME: ctx.ModuleName(module),
281 ComplianceMetadataProp.MODULE_TYPE: ctx.ModuleType(module),
282 ComplianceMetadataProp.PKG_DEFAULT_APPLICABLE_LICENSES: strings.Join(module.base().primaryLicensesProperty.getStrings(), " "),
283 }
284 rowId = rowId + 1
285 metadata := []string{strconv.Itoa(rowId)}
286 for _, propertyName := range COMPLIANCE_METADATA_PROPS {
287 metadata = append(metadata, metadataMap[propertyName])
288 }
289 writerToCsv(csvWriter, metadata)
290 return
291 }
Yu Liu663e4502024-08-12 18:23:59 +0000292 if provider, ok := ctx.otherModuleProvider(module, ComplianceMetadataProvider); ok {
Wei Lia1aa2972024-06-21 13:08:51 -0700293 metadataInfo := provider.(*ComplianceMetadataInfo)
294 rowId = rowId + 1
295 metadata := []string{strconv.Itoa(rowId)}
296 for _, propertyName := range COMPLIANCE_METADATA_PROPS {
297 metadata = append(metadata, metadataInfo.getStringValue(propertyName))
298 }
299 writerToCsv(csvWriter, metadata)
300 return
301 }
302 })
303 csvWriter.Flush()
304
305 deviceProduct := ctx.Config().DeviceProduct()
306 modulesCsv := PathForOutput(ctx, "compliance-metadata", deviceProduct, "soong-modules.csv")
307 WriteFileRuleVerbatim(ctx, modulesCsv, buffer.String())
308
309 // Metadata generated in Make
310 makeMetadataCsv := PathForOutput(ctx, "compliance-metadata", deviceProduct, "make-metadata.csv")
311 makeModulesCsv := PathForOutput(ctx, "compliance-metadata", deviceProduct, "make-modules.csv")
312
313 // Import metadata from Make and Soong to sqlite3 database
314 complianceMetadataDb := PathForOutput(ctx, "compliance-metadata", deviceProduct, "compliance-metadata.db")
315 ctx.Build(pctx, BuildParams{
316 Rule: importCsv,
317 Input: modulesCsv,
318 Implicits: []Path{
319 makeMetadataCsv,
320 makeModulesCsv,
321 },
322 Output: complianceMetadataDb,
323 Args: map[string]string{
324 "make_metadata": makeMetadataCsv.String(),
325 "make_modules": makeModulesCsv.String(),
326 },
327 })
328
329 // Phony rule "compliance-metadata.db". "m compliance-metadata.db" to create the compliance metadata database.
330 ctx.Build(pctx, BuildParams{
331 Rule: blueprint.Phony,
332 Inputs: []Path{complianceMetadataDb},
333 Output: PathForPhony(ctx, "compliance-metadata.db"),
334 })
335
336}