1// Copyright (C) 2019 The Android Open Source Project
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
15// sysprop package defines a module named sysprop_library that can implement sysprop as API
16// See https://source.android.com/devices/architecture/sysprops-apis for details
17package sysprop
18
19import (
20	"fmt"
21	"io"
22	"os"
23	"path"
24	"sync"
25
26	"github.com/google/blueprint"
27	"github.com/google/blueprint/proptools"
28
29	"android/soong/android"
30	"android/soong/cc"
31	"android/soong/java"
32)
33
34type dependencyTag struct {
35	blueprint.BaseDependencyTag
36	name string
37}
38
39type syspropGenProperties struct {
40	Srcs      []string `android:"path"`
41	Scope     string
42	Name      *string
43	Check_api *string
44}
45
46type syspropJavaGenRule struct {
47	android.ModuleBase
48
49	properties syspropGenProperties
50
51	genSrcjars android.Paths
52}
53
54var _ android.OutputFileProducer = (*syspropJavaGenRule)(nil)
55
56var (
57	syspropJava = pctx.AndroidStaticRule("syspropJava",
58		blueprint.RuleParams{
59			Command: `rm -rf $out.tmp && mkdir -p $out.tmp && ` +
60				`$syspropJavaCmd --scope $scope --java-output-dir $out.tmp $in && ` +
61				`$soongZipCmd -jar -o $out -C $out.tmp -D $out.tmp && rm -rf $out.tmp`,
62			CommandDeps: []string{
63				"$syspropJavaCmd",
64				"$soongZipCmd",
65			},
66		}, "scope")
67)
68
69func init() {
70	pctx.HostBinToolVariable("soongZipCmd", "soong_zip")
71	pctx.HostBinToolVariable("syspropJavaCmd", "sysprop_java")
72}
73
74// syspropJavaGenRule module generates srcjar containing generated java APIs.
75// It also depends on check api rule, so api check has to pass to use sysprop_library.
76func (g *syspropJavaGenRule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
77	var checkApiFileTimeStamp android.WritablePath
78
79	ctx.VisitDirectDeps(func(dep android.Module) {
80		if m, ok := dep.(*syspropLibrary); ok {
81			checkApiFileTimeStamp = m.checkApiFileTimeStamp
82		}
83	})
84
85	for _, syspropFile := range android.PathsForModuleSrc(ctx, g.properties.Srcs) {
86		srcJarFile := android.GenPathWithExt(ctx, "sysprop", syspropFile, "srcjar")
87
88		ctx.Build(pctx, android.BuildParams{
89			Rule:        syspropJava,
90			Description: "sysprop_java " + syspropFile.Rel(),
91			Output:      srcJarFile,
92			Input:       syspropFile,
93			Implicit:    checkApiFileTimeStamp,
94			Args: map[string]string{
95				"scope": g.properties.Scope,
96			},
97		})
98
99		g.genSrcjars = append(g.genSrcjars, srcJarFile)
100	}
101}
102
103func (g *syspropJavaGenRule) DepsMutator(ctx android.BottomUpMutatorContext) {
104	// Add a dependency from the stubs to sysprop library so that the generator rule can depend on
105	// the check API rule of the sysprop library.
106	ctx.AddFarVariationDependencies(nil, nil, proptools.String(g.properties.Check_api))
107}
108
109func (g *syspropJavaGenRule) OutputFiles(tag string) (android.Paths, error) {
110	switch tag {
111	case "":
112		return g.genSrcjars, nil
113	default:
114		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
115	}
116}
117
118func syspropJavaGenFactory() android.Module {
119	g := &syspropJavaGenRule{}
120	g.AddProperties(&g.properties)
121	android.InitAndroidModule(g)
122	return g
123}
124
125type syspropLibrary struct {
126	android.ModuleBase
127	android.ApexModuleBase
128
129	properties syspropLibraryProperties
130
131	checkApiFileTimeStamp android.WritablePath
132	latestApiFile         android.OptionalPath
133	currentApiFile        android.OptionalPath
134	dumpedApiFile         android.WritablePath
135}
136
137type syspropLibraryProperties struct {
138	// Determine who owns this sysprop library. Possible values are
139	// "Platform", "Vendor", or "Odm"
140	Property_owner string
141
142	// list of package names that will be documented and publicized as API
143	Api_packages []string
144
145	// If set to true, allow this module to be dexed and installed on devices.
146	Installable *bool
147
148	// Make this module available when building for recovery
149	Recovery_available *bool
150
151	// Make this module available when building for vendor
152	Vendor_available *bool
153
154	// Make this module available when building for product
155	Product_available *bool
156
157	// list of .sysprop files which defines the properties.
158	Srcs []string `android:"path"`
159
160	// If set to true, build a variant of the module for the host.  Defaults to false.
161	Host_supported *bool
162
163	Cpp struct {
164		// Minimum sdk version that the artifact should support when it runs as part of mainline modules(APEX).
165		// Forwarded to cc_library.min_sdk_version
166		Min_sdk_version *string
167	}
168
169	Java struct {
170		// Minimum sdk version that the artifact should support when it runs as part of mainline modules(APEX).
171		// Forwarded to java_library.min_sdk_version
172		Min_sdk_version *string
173	}
174}
175
176var (
177	pctx         = android.NewPackageContext("android/soong/sysprop")
178	syspropCcTag = dependencyTag{name: "syspropCc"}
179
180	syspropLibrariesKey  = android.NewOnceKey("syspropLibraries")
181	syspropLibrariesLock sync.Mutex
182)
183
184// List of sysprop_library used by property_contexts to perform type check.
185func syspropLibraries(config android.Config) *[]string {
186	return config.Once(syspropLibrariesKey, func() interface{} {
187		return &[]string{}
188	}).(*[]string)
189}
190
191func SyspropLibraries(config android.Config) []string {
192	return append([]string{}, *syspropLibraries(config)...)
193}
194
195func init() {
196	registerSyspropBuildComponents(android.InitRegistrationContext)
197}
198
199func registerSyspropBuildComponents(ctx android.RegistrationContext) {
200	ctx.RegisterModuleType("sysprop_library", syspropLibraryFactory)
201}
202
203func (m *syspropLibrary) Name() string {
204	return m.BaseModuleName() + "_sysprop_library"
205}
206
207func (m *syspropLibrary) Owner() string {
208	return m.properties.Property_owner
209}
210
211func (m *syspropLibrary) CcImplementationModuleName() string {
212	return "lib" + m.BaseModuleName()
213}
214
215func (m *syspropLibrary) javaPublicStubName() string {
216	return m.BaseModuleName() + "_public"
217}
218
219func (m *syspropLibrary) javaGenModuleName() string {
220	return m.BaseModuleName() + "_java_gen"
221}
222
223func (m *syspropLibrary) javaGenPublicStubName() string {
224	return m.BaseModuleName() + "_java_gen_public"
225}
226
227func (m *syspropLibrary) BaseModuleName() string {
228	return m.ModuleBase.Name()
229}
230
231func (m *syspropLibrary) CurrentSyspropApiFile() android.OptionalPath {
232	return m.currentApiFile
233}
234
235// GenerateAndroidBuildActions of sysprop_library handles API dump and API check.
236// generated java_library will depend on these API files.
237func (m *syspropLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
238	baseModuleName := m.BaseModuleName()
239
240	for _, syspropFile := range android.PathsForModuleSrc(ctx, m.properties.Srcs) {
241		if syspropFile.Ext() != ".sysprop" {
242			ctx.PropertyErrorf("srcs", "srcs contains non-sysprop file %q", syspropFile.String())
243		}
244	}
245
246	if ctx.Failed() {
247		return
248	}
249
250	apiDirectoryPath := path.Join(ctx.ModuleDir(), "api")
251	currentApiFilePath := path.Join(apiDirectoryPath, baseModuleName+"-current.txt")
252	latestApiFilePath := path.Join(apiDirectoryPath, baseModuleName+"-latest.txt")
253	m.currentApiFile = android.ExistentPathForSource(ctx, currentApiFilePath)
254	m.latestApiFile = android.ExistentPathForSource(ctx, latestApiFilePath)
255
256	// dump API rule
257	rule := android.NewRuleBuilder(pctx, ctx)
258	m.dumpedApiFile = android.PathForModuleOut(ctx, "api-dump.txt")
259	rule.Command().
260		BuiltTool("sysprop_api_dump").
261		Output(m.dumpedApiFile).
262		Inputs(android.PathsForModuleSrc(ctx, m.properties.Srcs))
263	rule.Build(baseModuleName+"_api_dump", baseModuleName+" api dump")
264
265	// check API rule
266	rule = android.NewRuleBuilder(pctx, ctx)
267
268	// We allow that the API txt files don't exist, when the sysprop_library only contains internal
269	// properties. But we have to feed current api file and latest api file to the rule builder.
270	// Currently we can't get android.Path representing the null device, so we add any existing API
271	// txt files to implicits, and then directly feed string paths, rather than calling Input(Path)
272	// method.
273	var apiFileList android.Paths
274	currentApiArgument := os.DevNull
275	if m.currentApiFile.Valid() {
276		apiFileList = append(apiFileList, m.currentApiFile.Path())
277		currentApiArgument = m.currentApiFile.String()
278	}
279
280	latestApiArgument := os.DevNull
281	if m.latestApiFile.Valid() {
282		apiFileList = append(apiFileList, m.latestApiFile.Path())
283		latestApiArgument = m.latestApiFile.String()
284	}
285
286	// 1. compares current.txt to api-dump.txt
287	// current.txt should be identical to api-dump.txt.
288	msg := fmt.Sprintf(`\n******************************\n`+
289		`API of sysprop_library %s doesn't match with current.txt\n`+
290		`Please update current.txt by:\n`+
291		`m %s-dump-api && mkdir -p %q && rm -rf %q && cp -f %q %q\n`+
292		`******************************\n`, baseModuleName, baseModuleName,
293		apiDirectoryPath, currentApiFilePath, m.dumpedApiFile.String(), currentApiFilePath)
294
295	rule.Command().
296		Text("( cmp").Flag("-s").
297		Input(m.dumpedApiFile).
298		Text(currentApiArgument).
299		Text("|| ( echo").Flag("-e").
300		Flag(`"` + msg + `"`).
301		Text("; exit 38) )")
302
303	// 2. compares current.txt to latest.txt (frozen API)
304	// current.txt should be compatible with latest.txt
305	msg = fmt.Sprintf(`\n******************************\n`+
306		`API of sysprop_library %s doesn't match with latest version\n`+
307		`Please fix the breakage and rebuild.\n`+
308		`******************************\n`, baseModuleName)
309
310	rule.Command().
311		Text("( ").
312		BuiltTool("sysprop_api_checker").
313		Text(latestApiArgument).
314		Text(currentApiArgument).
315		Text(" || ( echo").Flag("-e").
316		Flag(`"` + msg + `"`).
317		Text("; exit 38) )").
318		Implicits(apiFileList)
319
320	m.checkApiFileTimeStamp = android.PathForModuleOut(ctx, "check_api.timestamp")
321
322	rule.Command().
323		Text("touch").
324		Output(m.checkApiFileTimeStamp)
325
326	rule.Build(baseModuleName+"_check_api", baseModuleName+" check api")
327}
328
329func (m *syspropLibrary) AndroidMk() android.AndroidMkData {
330	return android.AndroidMkData{
331		Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
332			// sysprop_library module itself is defined as a FAKE module to perform API check.
333			// Actual implementation libraries are created on LoadHookMutator
334			fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)")
335			fmt.Fprintf(w, "LOCAL_MODULE := %s\n", m.Name())
336			data.Entries.WriteLicenseVariables(w)
337			fmt.Fprintf(w, "LOCAL_MODULE_CLASS := FAKE\n")
338			fmt.Fprintf(w, "LOCAL_MODULE_TAGS := optional\n")
339			fmt.Fprintf(w, "include $(BUILD_SYSTEM)/base_rules.mk\n\n")
340			fmt.Fprintf(w, "$(LOCAL_BUILT_MODULE): %s\n", m.checkApiFileTimeStamp.String())
341			fmt.Fprintf(w, "\ttouch $@\n\n")
342			fmt.Fprintf(w, ".PHONY: %s-check-api %s-dump-api\n\n", name, name)
343
344			// dump API rule
345			fmt.Fprintf(w, "%s-dump-api: %s\n\n", name, m.dumpedApiFile.String())
346
347			// check API rule
348			fmt.Fprintf(w, "%s-check-api: %s\n\n", name, m.checkApiFileTimeStamp.String())
349		}}
350}
351
352var _ android.ApexModule = (*syspropLibrary)(nil)
353
354// Implements android.ApexModule
355func (m *syspropLibrary) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
356	sdkVersion android.ApiLevel) error {
357	return fmt.Errorf("sysprop_library is not supposed to be part of apex modules")
358}
359
360// sysprop_library creates schematized APIs from sysprop description files (.sysprop).
361// Both Java and C++ modules can link against sysprop_library, and API stability check
362// against latest APIs (see build/soong/scripts/freeze-sysprop-api-files.sh)
363// is performed.
364func syspropLibraryFactory() android.Module {
365	m := &syspropLibrary{}
366
367	m.AddProperties(
368		&m.properties,
369	)
370	android.InitAndroidModule(m)
371	android.InitApexModule(m)
372	android.AddLoadHook(m, func(ctx android.LoadHookContext) { syspropLibraryHook(ctx, m) })
373	return m
374}
375
376type ccLibraryProperties struct {
377	Name             *string
378	Srcs             []string
379	Soc_specific     *bool
380	Device_specific  *bool
381	Product_specific *bool
382	Sysprop          struct {
383		Platform *bool
384	}
385	Target struct {
386		Android struct {
387			Header_libs []string
388			Shared_libs []string
389		}
390		Host struct {
391			Static_libs []string
392		}
393	}
394	Required           []string
395	Recovery           *bool
396	Recovery_available *bool
397	Vendor_available   *bool
398	Product_available  *bool
399	Host_supported     *bool
400	Apex_available     []string
401	Min_sdk_version    *string
402}
403
404type javaLibraryProperties struct {
405	Name              *string
406	Srcs              []string
407	Soc_specific      *bool
408	Device_specific   *bool
409	Product_specific  *bool
410	Required          []string
411	Sdk_version       *string
412	Installable       *bool
413	Libs              []string
414	Stem              *string
415	SyspropPublicStub string
416	Apex_available    []string
417	Min_sdk_version   *string
418}
419
420func syspropLibraryHook(ctx android.LoadHookContext, m *syspropLibrary) {
421	if len(m.properties.Srcs) == 0 {
422		ctx.PropertyErrorf("srcs", "sysprop_library must specify srcs")
423	}
424
425	// ctx's Platform or Specific functions represent where this sysprop_library installed.
426	installedInSystem := ctx.Platform() || ctx.SystemExtSpecific()
427	installedInVendorOrOdm := ctx.SocSpecific() || ctx.DeviceSpecific()
428	installedInProduct := ctx.ProductSpecific()
429	isOwnerPlatform := false
430	var javaSyspropStub string
431
432	// javaSyspropStub contains stub libraries used by generated APIs, instead of framework stub.
433	// This is to make sysprop_library link against core_current.
434	if installedInVendorOrOdm {
435		javaSyspropStub = "sysprop-library-stub-vendor"
436	} else if installedInProduct {
437		javaSyspropStub = "sysprop-library-stub-product"
438	} else {
439		javaSyspropStub = "sysprop-library-stub-platform"
440	}
441
442	switch m.Owner() {
443	case "Platform":
444		// Every partition can access platform-defined properties
445		isOwnerPlatform = true
446	case "Vendor":
447		// System can't access vendor's properties
448		if installedInSystem {
449			ctx.ModuleErrorf("None of soc_specific, device_specific, product_specific is true. " +
450				"System can't access sysprop_library owned by Vendor")
451		}
452	case "Odm":
453		// Only vendor can access Odm-defined properties
454		if !installedInVendorOrOdm {
455			ctx.ModuleErrorf("Neither soc_speicifc nor device_specific is true. " +
456				"Odm-defined properties should be accessed only in Vendor or Odm")
457		}
458	default:
459		ctx.PropertyErrorf("property_owner",
460			"Unknown value %s: must be one of Platform, Vendor or Odm", m.Owner())
461	}
462
463	// Generate a C++ implementation library.
464	// cc_library can receive *.sysprop files as their srcs, generating sources itself.
465	ccProps := ccLibraryProperties{}
466	ccProps.Name = proptools.StringPtr(m.CcImplementationModuleName())
467	ccProps.Srcs = m.properties.Srcs
468	ccProps.Soc_specific = proptools.BoolPtr(ctx.SocSpecific())
469	ccProps.Device_specific = proptools.BoolPtr(ctx.DeviceSpecific())
470	ccProps.Product_specific = proptools.BoolPtr(ctx.ProductSpecific())
471	ccProps.Sysprop.Platform = proptools.BoolPtr(isOwnerPlatform)
472	ccProps.Target.Android.Header_libs = []string{"libbase_headers"}
473	ccProps.Target.Android.Shared_libs = []string{"liblog"}
474	ccProps.Target.Host.Static_libs = []string{"libbase", "liblog"}
475	ccProps.Recovery_available = m.properties.Recovery_available
476	ccProps.Vendor_available = m.properties.Vendor_available
477	ccProps.Product_available = m.properties.Product_available
478	ccProps.Host_supported = m.properties.Host_supported
479	ccProps.Apex_available = m.ApexProperties.Apex_available
480	ccProps.Min_sdk_version = m.properties.Cpp.Min_sdk_version
481	ctx.CreateModule(cc.LibraryFactory, &ccProps)
482
483	scope := "internal"
484
485	// We need to only use public version, if the partition where sysprop_library will be installed
486	// is different from owner.
487	if ctx.ProductSpecific() {
488		// Currently product partition can't own any sysprop_library. So product always uses public.
489		scope = "public"
490	} else if isOwnerPlatform && installedInVendorOrOdm {
491		// Vendor or Odm should use public version of Platform's sysprop_library.
492		scope = "public"
493	}
494
495	// Generate a Java implementation library.
496	// Contrast to C++, syspropJavaGenRule module will generate srcjar and the srcjar will be fed
497	// to Java implementation library.
498	ctx.CreateModule(syspropJavaGenFactory, &syspropGenProperties{
499		Srcs:      m.properties.Srcs,
500		Scope:     scope,
501		Name:      proptools.StringPtr(m.javaGenModuleName()),
502		Check_api: proptools.StringPtr(ctx.ModuleName()),
503	})
504
505	// if platform sysprop_library is installed in /system or /system-ext, we regard it as an API
506	// and allow any modules (even from different partition) to link against the sysprop_library.
507	// To do that, we create a public stub and expose it to modules with sdk_version: system_*.
508	var publicStub string
509	if isOwnerPlatform && installedInSystem {
510		publicStub = m.javaPublicStubName()
511	}
512
513	ctx.CreateModule(java.LibraryFactory, &javaLibraryProperties{
514		Name:              proptools.StringPtr(m.BaseModuleName()),
515		Srcs:              []string{":" + m.javaGenModuleName()},
516		Soc_specific:      proptools.BoolPtr(ctx.SocSpecific()),
517		Device_specific:   proptools.BoolPtr(ctx.DeviceSpecific()),
518		Product_specific:  proptools.BoolPtr(ctx.ProductSpecific()),
519		Installable:       m.properties.Installable,
520		Sdk_version:       proptools.StringPtr("core_current"),
521		Libs:              []string{javaSyspropStub},
522		SyspropPublicStub: publicStub,
523		Apex_available:    m.ApexProperties.Apex_available,
524		Min_sdk_version:   m.properties.Java.Min_sdk_version,
525	})
526
527	if publicStub != "" {
528		ctx.CreateModule(syspropJavaGenFactory, &syspropGenProperties{
529			Srcs:      m.properties.Srcs,
530			Scope:     "public",
531			Name:      proptools.StringPtr(m.javaGenPublicStubName()),
532			Check_api: proptools.StringPtr(ctx.ModuleName()),
533		})
534
535		ctx.CreateModule(java.LibraryFactory, &javaLibraryProperties{
536			Name:        proptools.StringPtr(publicStub),
537			Srcs:        []string{":" + m.javaGenPublicStubName()},
538			Installable: proptools.BoolPtr(false),
539			Sdk_version: proptools.StringPtr("core_current"),
540			Libs:        []string{javaSyspropStub},
541			Stem:        proptools.StringPtr(m.BaseModuleName()),
542		})
543	}
544
545	// syspropLibraries will be used by property_contexts to check types.
546	// Record absolute paths of sysprop_library to prevent soong_namespace problem.
547	if m.ExportedToMake() {
548		syspropLibrariesLock.Lock()
549		defer syspropLibrariesLock.Unlock()
550
551		libraries := syspropLibraries(ctx.Config())
552		*libraries = append(*libraries, "//"+ctx.ModuleDir()+":"+ctx.ModuleName())
553	}
554}
555