blob: 7c0ab85c34602f0448e4a5cc613cca384aa39cd0 [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"
Yu Liu3cadf7d2024-10-24 18:47:06 +000026 "github.com/google/blueprint/gobtools"
Wei Lia1aa2972024-06-21 13:08:51 -070027)
28
29var (
30 // Constants of property names used in compliance metadata of modules
31 ComplianceMetadataProp = struct {
32 NAME string
33 PACKAGE string
34 MODULE_TYPE string
35 OS string
36 ARCH string
37 IS_PRIMARY_ARCH string
38 VARIANT string
39 IS_STATIC_LIB string
40 INSTALLED_FILES string
41 BUILT_FILES string
42 STATIC_DEPS string
43 STATIC_DEP_FILES string
44 WHOLE_STATIC_DEPS string
45 WHOLE_STATIC_DEP_FILES string
Wei Li8ede4f72025-01-15 21:24:22 -080046 HEADER_LIBS string
Wei Lia1aa2972024-06-21 13:08:51 -070047 LICENSES string
48
49 // module_type=package
50 PKG_DEFAULT_APPLICABLE_LICENSES string
51
52 // module_type=license
53 LIC_LICENSE_KINDS string
54 LIC_LICENSE_TEXT string
55 LIC_PACKAGE_NAME string
56
57 // module_type=license_kind
58 LK_CONDITIONS string
59 LK_URL string
60 }{
61 "name",
62 "package",
63 "module_type",
64 "os",
65 "arch",
66 "is_primary_arch",
67 "variant",
68 "is_static_lib",
69 "installed_files",
70 "built_files",
71 "static_deps",
72 "static_dep_files",
73 "whole_static_deps",
74 "whole_static_dep_files",
Wei Li8ede4f72025-01-15 21:24:22 -080075 "header_libs",
Wei Lia1aa2972024-06-21 13:08:51 -070076 "licenses",
77
78 "pkg_default_applicable_licenses",
79
80 "lic_license_kinds",
81 "lic_license_text",
82 "lic_package_name",
83
84 "lk_conditions",
85 "lk_url",
86 }
87
88 // A constant list of all property names in compliance metadata
89 // Order of properties here is the order of columns in the exported CSV file.
90 COMPLIANCE_METADATA_PROPS = []string{
91 ComplianceMetadataProp.NAME,
92 ComplianceMetadataProp.PACKAGE,
93 ComplianceMetadataProp.MODULE_TYPE,
94 ComplianceMetadataProp.OS,
95 ComplianceMetadataProp.ARCH,
96 ComplianceMetadataProp.VARIANT,
97 ComplianceMetadataProp.IS_STATIC_LIB,
98 ComplianceMetadataProp.IS_PRIMARY_ARCH,
99 // Space separated installed files
100 ComplianceMetadataProp.INSTALLED_FILES,
101 // Space separated built files
102 ComplianceMetadataProp.BUILT_FILES,
103 // Space separated module names of static dependencies
104 ComplianceMetadataProp.STATIC_DEPS,
105 // Space separated file paths of static dependencies
106 ComplianceMetadataProp.STATIC_DEP_FILES,
107 // Space separated module names of whole static dependencies
108 ComplianceMetadataProp.WHOLE_STATIC_DEPS,
109 // Space separated file paths of whole static dependencies
110 ComplianceMetadataProp.WHOLE_STATIC_DEP_FILES,
Wei Li8ede4f72025-01-15 21:24:22 -0800111 // Space separated modules name of header libs
112 ComplianceMetadataProp.HEADER_LIBS,
Wei Lia1aa2972024-06-21 13:08:51 -0700113 ComplianceMetadataProp.LICENSES,
114 // module_type=package
115 ComplianceMetadataProp.PKG_DEFAULT_APPLICABLE_LICENSES,
116 // module_type=license
117 ComplianceMetadataProp.LIC_LICENSE_KINDS,
118 ComplianceMetadataProp.LIC_LICENSE_TEXT, // resolve to file paths
119 ComplianceMetadataProp.LIC_PACKAGE_NAME,
120 // module_type=license_kind
121 ComplianceMetadataProp.LK_CONDITIONS,
122 ComplianceMetadataProp.LK_URL,
123 }
124)
125
126// ComplianceMetadataInfo provides all metadata of a module, e.g. name, module type, package, license,
127// dependencies, built/installed files, etc. It is a wrapper on a map[string]string with some utility
128// methods to get/set properties' values.
129type ComplianceMetadataInfo struct {
Wei Li7b8455f2025-03-05 16:05:51 -0800130 properties map[string]string
131 filesContained []string
Wei Lia1aa2972024-06-21 13:08:51 -0700132}
133
Yu Liu467d7c52024-09-18 21:54:44 +0000134type complianceMetadataInfoGob struct {
Wei Li7b8455f2025-03-05 16:05:51 -0800135 Properties map[string]string
136 FilesContained []string
Yu Liu467d7c52024-09-18 21:54:44 +0000137}
138
Wei Lia1aa2972024-06-21 13:08:51 -0700139func NewComplianceMetadataInfo() *ComplianceMetadataInfo {
140 return &ComplianceMetadataInfo{
Wei Li7b8455f2025-03-05 16:05:51 -0800141 properties: map[string]string{},
142 filesContained: make([]string, 0),
Wei Lia1aa2972024-06-21 13:08:51 -0700143 }
144}
145
Yu Liu467d7c52024-09-18 21:54:44 +0000146func (m *ComplianceMetadataInfo) ToGob() *complianceMetadataInfoGob {
147 return &complianceMetadataInfoGob{
Wei Li7b8455f2025-03-05 16:05:51 -0800148 Properties: m.properties,
149 FilesContained: m.filesContained,
Yu Liu26a716d2024-08-30 23:40:32 +0000150 }
Yu Liu467d7c52024-09-18 21:54:44 +0000151}
Yu Liu26a716d2024-08-30 23:40:32 +0000152
Yu Liu467d7c52024-09-18 21:54:44 +0000153func (m *ComplianceMetadataInfo) FromGob(data *complianceMetadataInfoGob) {
154 m.properties = data.Properties
Wei Li7b8455f2025-03-05 16:05:51 -0800155 m.filesContained = data.FilesContained
Yu Liu467d7c52024-09-18 21:54:44 +0000156}
157
158func (c *ComplianceMetadataInfo) GobEncode() ([]byte, error) {
Yu Liu3cadf7d2024-10-24 18:47:06 +0000159 return gobtools.CustomGobEncode[complianceMetadataInfoGob](c)
Yu Liu26a716d2024-08-30 23:40:32 +0000160}
161
162func (c *ComplianceMetadataInfo) GobDecode(data []byte) error {
Yu Liu3cadf7d2024-10-24 18:47:06 +0000163 return gobtools.CustomGobDecode[complianceMetadataInfoGob](data, c)
Yu Liu26a716d2024-08-30 23:40:32 +0000164}
165
Wei Lia1aa2972024-06-21 13:08:51 -0700166func (c *ComplianceMetadataInfo) SetStringValue(propertyName string, value string) {
167 if !slices.Contains(COMPLIANCE_METADATA_PROPS, propertyName) {
168 panic(fmt.Errorf("Unknown metadata property: %s.", propertyName))
169 }
170 c.properties[propertyName] = value
171}
172
173func (c *ComplianceMetadataInfo) SetListValue(propertyName string, value []string) {
174 c.SetStringValue(propertyName, strings.TrimSpace(strings.Join(value, " ")))
175}
176
Wei Li7b8455f2025-03-05 16:05:51 -0800177func (c *ComplianceMetadataInfo) SetFilesContained(files []string) {
178 c.filesContained = files
179}
180
181func (c *ComplianceMetadataInfo) GetFilesContained() []string {
182 return c.filesContained
183}
184
Wei Lia1aa2972024-06-21 13:08:51 -0700185func (c *ComplianceMetadataInfo) getStringValue(propertyName string) string {
186 if !slices.Contains(COMPLIANCE_METADATA_PROPS, propertyName) {
187 panic(fmt.Errorf("Unknown metadata property: %s.", propertyName))
188 }
189 return c.properties[propertyName]
190}
191
192func (c *ComplianceMetadataInfo) getAllValues() map[string]string {
193 return c.properties
194}
195
196var (
197 ComplianceMetadataProvider = blueprint.NewProvider[*ComplianceMetadataInfo]()
198)
199
200// buildComplianceMetadataProvider starts with the ModuleContext.ComplianceMetadataInfo() and fills in more common metadata
201// for different module types without accessing their private fields but through android.Module interface
202// and public/private fields of package android. The final metadata is stored to a module's ComplianceMetadataProvider.
Yu Liuddc28332024-08-09 22:48:30 +0000203func buildComplianceMetadataProvider(ctx *moduleContext, m *ModuleBase) {
Wei Lia1aa2972024-06-21 13:08:51 -0700204 complianceMetadataInfo := ctx.ComplianceMetadataInfo()
205 complianceMetadataInfo.SetStringValue(ComplianceMetadataProp.NAME, m.Name())
206 complianceMetadataInfo.SetStringValue(ComplianceMetadataProp.PACKAGE, ctx.ModuleDir())
207 complianceMetadataInfo.SetStringValue(ComplianceMetadataProp.MODULE_TYPE, ctx.ModuleType())
208
209 switch ctx.ModuleType() {
210 case "license":
211 licenseModule := m.module.(*licenseModule)
212 complianceMetadataInfo.SetListValue(ComplianceMetadataProp.LIC_LICENSE_KINDS, licenseModule.properties.License_kinds)
213 complianceMetadataInfo.SetListValue(ComplianceMetadataProp.LIC_LICENSE_TEXT, PathsForModuleSrc(ctx, licenseModule.properties.License_text).Strings())
214 complianceMetadataInfo.SetStringValue(ComplianceMetadataProp.LIC_PACKAGE_NAME, String(licenseModule.properties.Package_name))
215 case "license_kind":
216 licenseKindModule := m.module.(*licenseKindModule)
217 complianceMetadataInfo.SetListValue(ComplianceMetadataProp.LK_CONDITIONS, licenseKindModule.properties.Conditions)
218 complianceMetadataInfo.SetStringValue(ComplianceMetadataProp.LK_URL, licenseKindModule.properties.Url)
219 default:
220 complianceMetadataInfo.SetStringValue(ComplianceMetadataProp.OS, ctx.Os().String())
221 complianceMetadataInfo.SetStringValue(ComplianceMetadataProp.ARCH, ctx.Arch().String())
222 complianceMetadataInfo.SetStringValue(ComplianceMetadataProp.IS_PRIMARY_ARCH, strconv.FormatBool(ctx.PrimaryArch()))
223 complianceMetadataInfo.SetStringValue(ComplianceMetadataProp.VARIANT, ctx.ModuleSubDir())
224 if m.primaryLicensesProperty != nil && m.primaryLicensesProperty.getName() == "licenses" {
225 complianceMetadataInfo.SetListValue(ComplianceMetadataProp.LICENSES, m.primaryLicensesProperty.getStrings())
226 }
227
228 var installed InstallPaths
Yu Liuddc28332024-08-09 22:48:30 +0000229 installed = append(installed, ctx.installFiles...)
Yu Liud46e5ae2024-08-15 18:46:17 +0000230 installed = append(installed, ctx.katiInstalls.InstallPaths()...)
231 installed = append(installed, ctx.katiSymlinks.InstallPaths()...)
Yu Liu82a6d142024-08-27 19:02:29 +0000232 installed = append(installed, ctx.katiInitRcInstalls.InstallPaths()...)
233 installed = append(installed, ctx.katiVintfInstalls.InstallPaths()...)
Wei Lia1aa2972024-06-21 13:08:51 -0700234 complianceMetadataInfo.SetListValue(ComplianceMetadataProp.INSTALLED_FILES, FirstUniqueStrings(installed.Strings()))
235 }
236 ctx.setProvider(ComplianceMetadataProvider, complianceMetadataInfo)
237}
238
239func init() {
240 RegisterComplianceMetadataSingleton(InitRegistrationContext)
241}
242
243func RegisterComplianceMetadataSingleton(ctx RegistrationContext) {
244 ctx.RegisterParallelSingletonType("compliance_metadata_singleton", complianceMetadataSingletonFactory)
245}
246
247var (
248 // sqlite3 command line tool
249 sqlite3 = pctx.HostBinToolVariable("sqlite3", "sqlite3")
250
251 // Command to import .csv files to sqlite3 database
252 importCsv = pctx.AndroidStaticRule("importCsv",
253 blueprint.RuleParams{
254 Command: `rm -rf $out && ` +
255 `${sqlite3} $out ".import --csv $in modules" && ` +
Wei Li9a8e1412025-02-05 15:50:29 -0800256 `${sqlite3} $out ".import --csv ${make_metadata} make_metadata" && ` +
257 `${sqlite3} $out ".import --csv ${make_modules} make_modules"`,
Wei Lia1aa2972024-06-21 13:08:51 -0700258 CommandDeps: []string{"${sqlite3}"},
259 }, "make_metadata", "make_modules")
260)
261
262func complianceMetadataSingletonFactory() Singleton {
263 return &complianceMetadataSingleton{}
264}
265
266type complianceMetadataSingleton struct {
267}
268
269func writerToCsv(csvWriter *csv.Writer, row []string) {
270 err := csvWriter.Write(row)
271 if err != nil {
272 panic(err)
273 }
274}
275
276// Collect compliance metadata from all Soong modules, write to a CSV file and
277// import compliance metadata from Make and Soong to a sqlite3 database.
278func (c *complianceMetadataSingleton) GenerateBuildActions(ctx SingletonContext) {
279 if !ctx.Config().HasDeviceProduct() {
280 return
281 }
282 var buffer bytes.Buffer
283 csvWriter := csv.NewWriter(&buffer)
284
285 // Collect compliance metadata of modules in Soong and write to out/soong/compliance-metadata/<product>/soong-modules.csv file.
286 columnNames := []string{"id"}
287 columnNames = append(columnNames, COMPLIANCE_METADATA_PROPS...)
288 writerToCsv(csvWriter, columnNames)
289
290 rowId := -1
Yu Liu367827f2025-02-15 00:18:33 +0000291 ctx.VisitAllModuleProxies(func(module ModuleProxy) {
292 commonInfo, _ := OtherModuleProvider(ctx, module, CommonModuleInfoKey)
293 if !commonInfo.Enabled {
Wei Lia1aa2972024-06-21 13:08:51 -0700294 return
295 }
Yu Liu367827f2025-02-15 00:18:33 +0000296
Wei Lia1aa2972024-06-21 13:08:51 -0700297 moduleType := ctx.ModuleType(module)
298 if moduleType == "package" {
299 metadataMap := map[string]string{
300 ComplianceMetadataProp.NAME: ctx.ModuleName(module),
301 ComplianceMetadataProp.MODULE_TYPE: ctx.ModuleType(module),
Yu Liu367827f2025-02-15 00:18:33 +0000302 ComplianceMetadataProp.PKG_DEFAULT_APPLICABLE_LICENSES: strings.Join(commonInfo.PrimaryLicensesProperty.getStrings(), " "),
Wei Lia1aa2972024-06-21 13:08:51 -0700303 }
304 rowId = rowId + 1
305 metadata := []string{strconv.Itoa(rowId)}
306 for _, propertyName := range COMPLIANCE_METADATA_PROPS {
307 metadata = append(metadata, metadataMap[propertyName])
308 }
309 writerToCsv(csvWriter, metadata)
310 return
311 }
Yu Liu367827f2025-02-15 00:18:33 +0000312 if metadataInfo, ok := OtherModuleProvider(ctx, module, ComplianceMetadataProvider); ok {
Wei Lia1aa2972024-06-21 13:08:51 -0700313 rowId = rowId + 1
314 metadata := []string{strconv.Itoa(rowId)}
315 for _, propertyName := range COMPLIANCE_METADATA_PROPS {
316 metadata = append(metadata, metadataInfo.getStringValue(propertyName))
317 }
318 writerToCsv(csvWriter, metadata)
319 return
320 }
321 })
322 csvWriter.Flush()
323
324 deviceProduct := ctx.Config().DeviceProduct()
325 modulesCsv := PathForOutput(ctx, "compliance-metadata", deviceProduct, "soong-modules.csv")
326 WriteFileRuleVerbatim(ctx, modulesCsv, buffer.String())
327
Wei Li9a8e1412025-02-05 15:50:29 -0800328 // Metadata generated in Make
329 makeMetadataCsv := PathForOutput(ctx, "compliance-metadata", deviceProduct, "make-metadata.csv")
330 makeModulesCsv := PathForOutput(ctx, "compliance-metadata", deviceProduct, "make-modules.csv")
Cole Faustdb0f85a2025-01-06 17:02:03 -0800331
Wei Li9a8e1412025-02-05 15:50:29 -0800332 if !ctx.Config().KatiEnabled() {
Wei Li7b8455f2025-03-05 16:05:51 -0800333 // In soong-only build the installed file list is from android_device module
334 ctx.VisitAllModuleProxies(func(module ModuleProxy) {
335 if androidDeviceInfo, ok := OtherModuleProvider(ctx, module, AndroidDeviceInfoProvider); !ok || !androidDeviceInfo.Main_device {
336 return
337 }
338 if metadataInfo, ok := OtherModuleProvider(ctx, module, ComplianceMetadataProvider); ok {
339 if len(metadataInfo.filesContained) > 0 {
340 csvHeaders := "installed_file,module_path,is_soong_module,is_prebuilt_make_module,product_copy_files,kernel_module_copy_files,is_platform_generated,static_libs,whole_static_libs,license_text"
341 csvContent := make([]string, 0, len(metadataInfo.filesContained)+1)
342 csvContent = append(csvContent, csvHeaders)
343 for _, file := range metadataInfo.filesContained {
344 csvContent = append(csvContent, file+",,Y,,,,,,,")
345 }
346 WriteFileRuleVerbatim(ctx, makeMetadataCsv, strings.Join(csvContent, "\n"))
347 WriteFileRuleVerbatim(ctx, makeModulesCsv, "name,module_path,module_class,module_type,static_libs,whole_static_libs,built_files,installed_files")
348 }
349 return
350 }
351 })
Cole Faustdb0f85a2025-01-06 17:02:03 -0800352 }
Wei Lia1aa2972024-06-21 13:08:51 -0700353
354 // Import metadata from Make and Soong to sqlite3 database
355 complianceMetadataDb := PathForOutput(ctx, "compliance-metadata", deviceProduct, "compliance-metadata.db")
356 ctx.Build(pctx, BuildParams{
Wei Li9a8e1412025-02-05 15:50:29 -0800357 Rule: importCsv,
358 Input: modulesCsv,
359 Implicits: []Path{
360 makeMetadataCsv,
361 makeModulesCsv,
362 },
363 Output: complianceMetadataDb,
364 Args: map[string]string{
365 "make_metadata": makeMetadataCsv.String(),
366 "make_modules": makeModulesCsv.String(),
367 },
Wei Lia1aa2972024-06-21 13:08:51 -0700368 })
369
370 // Phony rule "compliance-metadata.db". "m compliance-metadata.db" to create the compliance metadata database.
371 ctx.Build(pctx, BuildParams{
372 Rule: blueprint.Phony,
373 Inputs: []Path{complianceMetadataDb},
374 Output: PathForPhony(ctx, "compliance-metadata.db"),
375 })
376
377}