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