blob: 35805a2a8255349c5243017b5066ec8d94d9c8e0 [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 {
130 properties map[string]string
131}
132
Yu Liu467d7c52024-09-18 21:54:44 +0000133type complianceMetadataInfoGob struct {
134 Properties map[string]string
135}
136
Wei Lia1aa2972024-06-21 13:08:51 -0700137func NewComplianceMetadataInfo() *ComplianceMetadataInfo {
138 return &ComplianceMetadataInfo{
139 properties: map[string]string{},
140 }
141}
142
Yu Liu467d7c52024-09-18 21:54:44 +0000143func (m *ComplianceMetadataInfo) ToGob() *complianceMetadataInfoGob {
144 return &complianceMetadataInfoGob{
145 Properties: m.properties,
Yu Liu26a716d2024-08-30 23:40:32 +0000146 }
Yu Liu467d7c52024-09-18 21:54:44 +0000147}
Yu Liu26a716d2024-08-30 23:40:32 +0000148
Yu Liu467d7c52024-09-18 21:54:44 +0000149func (m *ComplianceMetadataInfo) FromGob(data *complianceMetadataInfoGob) {
150 m.properties = data.Properties
151}
152
153func (c *ComplianceMetadataInfo) GobEncode() ([]byte, error) {
Yu Liu3cadf7d2024-10-24 18:47:06 +0000154 return gobtools.CustomGobEncode[complianceMetadataInfoGob](c)
Yu Liu26a716d2024-08-30 23:40:32 +0000155}
156
157func (c *ComplianceMetadataInfo) GobDecode(data []byte) error {
Yu Liu3cadf7d2024-10-24 18:47:06 +0000158 return gobtools.CustomGobDecode[complianceMetadataInfoGob](data, c)
Yu Liu26a716d2024-08-30 23:40:32 +0000159}
160
Wei Lia1aa2972024-06-21 13:08:51 -0700161func (c *ComplianceMetadataInfo) SetStringValue(propertyName string, value string) {
162 if !slices.Contains(COMPLIANCE_METADATA_PROPS, propertyName) {
163 panic(fmt.Errorf("Unknown metadata property: %s.", propertyName))
164 }
165 c.properties[propertyName] = value
166}
167
168func (c *ComplianceMetadataInfo) SetListValue(propertyName string, value []string) {
169 c.SetStringValue(propertyName, strings.TrimSpace(strings.Join(value, " ")))
170}
171
172func (c *ComplianceMetadataInfo) getStringValue(propertyName string) string {
173 if !slices.Contains(COMPLIANCE_METADATA_PROPS, propertyName) {
174 panic(fmt.Errorf("Unknown metadata property: %s.", propertyName))
175 }
176 return c.properties[propertyName]
177}
178
179func (c *ComplianceMetadataInfo) getAllValues() map[string]string {
180 return c.properties
181}
182
183var (
184 ComplianceMetadataProvider = blueprint.NewProvider[*ComplianceMetadataInfo]()
185)
186
187// buildComplianceMetadataProvider starts with the ModuleContext.ComplianceMetadataInfo() and fills in more common metadata
188// for different module types without accessing their private fields but through android.Module interface
189// and public/private fields of package android. The final metadata is stored to a module's ComplianceMetadataProvider.
Yu Liuddc28332024-08-09 22:48:30 +0000190func buildComplianceMetadataProvider(ctx *moduleContext, m *ModuleBase) {
Wei Lia1aa2972024-06-21 13:08:51 -0700191 complianceMetadataInfo := ctx.ComplianceMetadataInfo()
192 complianceMetadataInfo.SetStringValue(ComplianceMetadataProp.NAME, m.Name())
193 complianceMetadataInfo.SetStringValue(ComplianceMetadataProp.PACKAGE, ctx.ModuleDir())
194 complianceMetadataInfo.SetStringValue(ComplianceMetadataProp.MODULE_TYPE, ctx.ModuleType())
195
196 switch ctx.ModuleType() {
197 case "license":
198 licenseModule := m.module.(*licenseModule)
199 complianceMetadataInfo.SetListValue(ComplianceMetadataProp.LIC_LICENSE_KINDS, licenseModule.properties.License_kinds)
200 complianceMetadataInfo.SetListValue(ComplianceMetadataProp.LIC_LICENSE_TEXT, PathsForModuleSrc(ctx, licenseModule.properties.License_text).Strings())
201 complianceMetadataInfo.SetStringValue(ComplianceMetadataProp.LIC_PACKAGE_NAME, String(licenseModule.properties.Package_name))
202 case "license_kind":
203 licenseKindModule := m.module.(*licenseKindModule)
204 complianceMetadataInfo.SetListValue(ComplianceMetadataProp.LK_CONDITIONS, licenseKindModule.properties.Conditions)
205 complianceMetadataInfo.SetStringValue(ComplianceMetadataProp.LK_URL, licenseKindModule.properties.Url)
206 default:
207 complianceMetadataInfo.SetStringValue(ComplianceMetadataProp.OS, ctx.Os().String())
208 complianceMetadataInfo.SetStringValue(ComplianceMetadataProp.ARCH, ctx.Arch().String())
209 complianceMetadataInfo.SetStringValue(ComplianceMetadataProp.IS_PRIMARY_ARCH, strconv.FormatBool(ctx.PrimaryArch()))
210 complianceMetadataInfo.SetStringValue(ComplianceMetadataProp.VARIANT, ctx.ModuleSubDir())
211 if m.primaryLicensesProperty != nil && m.primaryLicensesProperty.getName() == "licenses" {
212 complianceMetadataInfo.SetListValue(ComplianceMetadataProp.LICENSES, m.primaryLicensesProperty.getStrings())
213 }
214
215 var installed InstallPaths
Yu Liuddc28332024-08-09 22:48:30 +0000216 installed = append(installed, ctx.installFiles...)
Yu Liud46e5ae2024-08-15 18:46:17 +0000217 installed = append(installed, ctx.katiInstalls.InstallPaths()...)
218 installed = append(installed, ctx.katiSymlinks.InstallPaths()...)
Yu Liu82a6d142024-08-27 19:02:29 +0000219 installed = append(installed, ctx.katiInitRcInstalls.InstallPaths()...)
220 installed = append(installed, ctx.katiVintfInstalls.InstallPaths()...)
Wei Lia1aa2972024-06-21 13:08:51 -0700221 complianceMetadataInfo.SetListValue(ComplianceMetadataProp.INSTALLED_FILES, FirstUniqueStrings(installed.Strings()))
222 }
223 ctx.setProvider(ComplianceMetadataProvider, complianceMetadataInfo)
224}
225
226func init() {
227 RegisterComplianceMetadataSingleton(InitRegistrationContext)
228}
229
230func RegisterComplianceMetadataSingleton(ctx RegistrationContext) {
231 ctx.RegisterParallelSingletonType("compliance_metadata_singleton", complianceMetadataSingletonFactory)
232}
233
234var (
235 // sqlite3 command line tool
236 sqlite3 = pctx.HostBinToolVariable("sqlite3", "sqlite3")
237
238 // Command to import .csv files to sqlite3 database
239 importCsv = pctx.AndroidStaticRule("importCsv",
240 blueprint.RuleParams{
241 Command: `rm -rf $out && ` +
242 `${sqlite3} $out ".import --csv $in modules" && ` +
Wei Li9a8e1412025-02-05 15:50:29 -0800243 `${sqlite3} $out ".import --csv ${make_metadata} make_metadata" && ` +
244 `${sqlite3} $out ".import --csv ${make_modules} make_modules"`,
Wei Lia1aa2972024-06-21 13:08:51 -0700245 CommandDeps: []string{"${sqlite3}"},
246 }, "make_metadata", "make_modules")
247)
248
249func complianceMetadataSingletonFactory() Singleton {
250 return &complianceMetadataSingleton{}
251}
252
253type complianceMetadataSingleton struct {
254}
255
256func writerToCsv(csvWriter *csv.Writer, row []string) {
257 err := csvWriter.Write(row)
258 if err != nil {
259 panic(err)
260 }
261}
262
263// Collect compliance metadata from all Soong modules, write to a CSV file and
264// import compliance metadata from Make and Soong to a sqlite3 database.
265func (c *complianceMetadataSingleton) GenerateBuildActions(ctx SingletonContext) {
266 if !ctx.Config().HasDeviceProduct() {
267 return
268 }
269 var buffer bytes.Buffer
270 csvWriter := csv.NewWriter(&buffer)
271
272 // Collect compliance metadata of modules in Soong and write to out/soong/compliance-metadata/<product>/soong-modules.csv file.
273 columnNames := []string{"id"}
274 columnNames = append(columnNames, COMPLIANCE_METADATA_PROPS...)
275 writerToCsv(csvWriter, columnNames)
276
277 rowId := -1
278 ctx.VisitAllModules(func(module Module) {
279 if !module.Enabled(ctx) {
280 return
281 }
282 moduleType := ctx.ModuleType(module)
283 if moduleType == "package" {
284 metadataMap := map[string]string{
285 ComplianceMetadataProp.NAME: ctx.ModuleName(module),
286 ComplianceMetadataProp.MODULE_TYPE: ctx.ModuleType(module),
287 ComplianceMetadataProp.PKG_DEFAULT_APPLICABLE_LICENSES: strings.Join(module.base().primaryLicensesProperty.getStrings(), " "),
288 }
289 rowId = rowId + 1
290 metadata := []string{strconv.Itoa(rowId)}
291 for _, propertyName := range COMPLIANCE_METADATA_PROPS {
292 metadata = append(metadata, metadataMap[propertyName])
293 }
294 writerToCsv(csvWriter, metadata)
295 return
296 }
Yu Liu663e4502024-08-12 18:23:59 +0000297 if provider, ok := ctx.otherModuleProvider(module, ComplianceMetadataProvider); ok {
Wei Lia1aa2972024-06-21 13:08:51 -0700298 metadataInfo := provider.(*ComplianceMetadataInfo)
299 rowId = rowId + 1
300 metadata := []string{strconv.Itoa(rowId)}
301 for _, propertyName := range COMPLIANCE_METADATA_PROPS {
302 metadata = append(metadata, metadataInfo.getStringValue(propertyName))
303 }
304 writerToCsv(csvWriter, metadata)
305 return
306 }
307 })
308 csvWriter.Flush()
309
310 deviceProduct := ctx.Config().DeviceProduct()
311 modulesCsv := PathForOutput(ctx, "compliance-metadata", deviceProduct, "soong-modules.csv")
312 WriteFileRuleVerbatim(ctx, modulesCsv, buffer.String())
313
Wei Li9a8e1412025-02-05 15:50:29 -0800314 // Metadata generated in Make
315 makeMetadataCsv := PathForOutput(ctx, "compliance-metadata", deviceProduct, "make-metadata.csv")
316 makeModulesCsv := PathForOutput(ctx, "compliance-metadata", deviceProduct, "make-modules.csv")
Cole Faustdb0f85a2025-01-06 17:02:03 -0800317
Wei Li9a8e1412025-02-05 15:50:29 -0800318 if !ctx.Config().KatiEnabled() {
Cole Faust9d1636c2025-02-06 16:00:51 -0800319 WriteFileRule(ctx, makeMetadataCsv, "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")
320 WriteFileRule(ctx, makeModulesCsv, "name,module_path,module_class,module_type,static_libs,whole_static_libs,built_files,installed_files")
Cole Faustdb0f85a2025-01-06 17:02:03 -0800321 }
Wei Lia1aa2972024-06-21 13:08:51 -0700322
323 // Import metadata from Make and Soong to sqlite3 database
324 complianceMetadataDb := PathForOutput(ctx, "compliance-metadata", deviceProduct, "compliance-metadata.db")
325 ctx.Build(pctx, BuildParams{
Wei Li9a8e1412025-02-05 15:50:29 -0800326 Rule: importCsv,
327 Input: modulesCsv,
328 Implicits: []Path{
329 makeMetadataCsv,
330 makeModulesCsv,
331 },
332 Output: complianceMetadataDb,
333 Args: map[string]string{
334 "make_metadata": makeMetadataCsv.String(),
335 "make_modules": makeModulesCsv.String(),
336 },
Wei Lia1aa2972024-06-21 13:08:51 -0700337 })
338
339 // Phony rule "compliance-metadata.db". "m compliance-metadata.db" to create the compliance metadata database.
340 ctx.Build(pctx, BuildParams{
341 Rule: blueprint.Phony,
342 Inputs: []Path{complianceMetadataDb},
343 Output: PathForPhony(ctx, "compliance-metadata.db"),
344 })
345
346}