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