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 apex
16
17import (
18	"fmt"
19	"io"
20	"path/filepath"
21	"strings"
22
23	"android/soong/android"
24	"android/soong/cc"
25	"android/soong/java"
26
27	"github.com/google/blueprint/proptools"
28)
29
30func (a *apexBundle) AndroidMk() android.AndroidMkData {
31	if a.properties.HideFromMake {
32		return android.AndroidMkData{
33			Disabled: true,
34		}
35	}
36	return a.androidMkForType()
37}
38
39// nameInMake converts apexFileClass into the corresponding class name in Make.
40func (class apexFileClass) nameInMake() string {
41	switch class {
42	case etc:
43		return "ETC"
44	case nativeSharedLib:
45		return "SHARED_LIBRARIES"
46	case nativeExecutable, shBinary, pyBinary, goBinary:
47		return "EXECUTABLES"
48	case javaSharedLib:
49		return "JAVA_LIBRARIES"
50	case nativeTest:
51		return "NATIVE_TESTS"
52	case app, appSet:
53		// b/142537672 Why isn't this APP? We want to have full control over
54		// the paths and file names of the apk file under the flattend APEX.
55		// If this is set to APP, then the paths and file names are modified
56		// by the Make build system. For example, it is installed to
57		// /system/apex/<apexname>/app/<Appname>/<apexname>.<Appname>/ instead of
58		// /system/apex/<apexname>/app/<Appname> because the build system automatically
59		// appends module name (which is <apexname>.<Appname> to the path.
60		return "ETC"
61	default:
62		panic(fmt.Errorf("unknown class %d", class))
63	}
64}
65
66// Return the full module name for a dependency module, which appends the apex module name unless re-using a system lib.
67func (a *apexBundle) fullModuleName(apexBundleName string, fi *apexFile) string {
68	linkToSystemLib := a.linkToSystemLib && fi.transitiveDep && fi.availableToPlatform()
69
70	if linkToSystemLib {
71		return fi.androidMkModuleName
72	}
73	return fi.androidMkModuleName + "." + apexBundleName + a.suffix
74}
75
76func (a *apexBundle) androidMkForFiles(w io.Writer, apexBundleName, apexName, moduleDir string,
77	apexAndroidMkData android.AndroidMkData) []string {
78
79	// apexBundleName comes from the 'name' property; apexName comes from 'apex_name' property.
80	// An apex is installed to /system/apex/<apexBundleName> and is activated at /apex/<apexName>
81	// In many cases, the two names are the same, but could be different in general.
82
83	moduleNames := []string{}
84	apexType := a.properties.ApexType
85	// To avoid creating duplicate build rules, run this function only when primaryApexType is true
86	// to install symbol files in $(PRODUCT_OUT}/apex.
87	// And if apexType is flattened, run this function to install files in $(PRODUCT_OUT}/system/apex.
88	if !a.primaryApexType && apexType != flattenedApex {
89		return moduleNames
90	}
91
92	// b/162366062. Prevent GKI APEXes to emit make rules to avoid conflicts.
93	if strings.HasPrefix(apexName, "com.android.gki.") && apexType != flattenedApex {
94		return moduleNames
95	}
96
97	// b/140136207. When there are overriding APEXes for a VNDK APEX, the symbols file for the overridden
98	// APEX and the overriding APEX will have the same installation paths at /apex/com.android.vndk.v<ver>
99	// as their apexName will be the same. To avoid the path conflicts, skip installing the symbol files
100	// for the overriding VNDK APEXes.
101	symbolFilesNotNeeded := a.vndkApex && len(a.overridableProperties.Overrides) > 0
102	if symbolFilesNotNeeded && apexType != flattenedApex {
103		return moduleNames
104	}
105
106	var postInstallCommands []string
107	for _, fi := range a.filesInfo {
108		if a.linkToSystemLib && fi.transitiveDep && fi.availableToPlatform() {
109			// TODO(jiyong): pathOnDevice should come from fi.module, not being calculated here
110			linkTarget := filepath.Join("/system", fi.path())
111			linkPath := filepath.Join(a.installDir.ToMakePath().String(), apexBundleName, fi.path())
112			mkdirCmd := "mkdir -p " + filepath.Dir(linkPath)
113			linkCmd := "ln -sfn " + linkTarget + " " + linkPath
114			postInstallCommands = append(postInstallCommands, mkdirCmd, linkCmd)
115		}
116	}
117
118	seenDataOutPaths := make(map[string]bool)
119
120	for _, fi := range a.filesInfo {
121		linkToSystemLib := a.linkToSystemLib && fi.transitiveDep && fi.availableToPlatform()
122
123		moduleName := a.fullModuleName(apexBundleName, &fi)
124
125		// This name will be added to LOCAL_REQUIRED_MODULES of the APEX. We need to be
126		// arch-specific otherwise we will end up installing both ABIs even when only
127		// either of the ABI is requested.
128		aName := moduleName
129		switch fi.multilib {
130		case "lib32":
131			aName = aName + ":32"
132		case "lib64":
133			aName = aName + ":64"
134		}
135		if !android.InList(aName, moduleNames) {
136			moduleNames = append(moduleNames, aName)
137		}
138
139		if linkToSystemLib {
140			// No need to copy the file since it's linked to the system file
141			continue
142		}
143
144		fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)")
145		if fi.moduleDir != "" {
146			fmt.Fprintln(w, "LOCAL_PATH :=", fi.moduleDir)
147		} else {
148			fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir)
149		}
150		fmt.Fprintln(w, "LOCAL_MODULE :=", moduleName)
151		if fi.module != nil && fi.module.Owner() != "" {
152			fmt.Fprintln(w, "LOCAL_MODULE_OWNER :=", fi.module.Owner())
153		}
154		// /apex/<apex_name>/{lib|framework|...}
155		pathWhenActivated := filepath.Join("$(PRODUCT_OUT)", "apex", apexName, fi.installDir)
156		var modulePath string
157		if apexType == flattenedApex {
158			// /system/apex/<name>/{lib|framework|...}
159			modulePath = filepath.Join(a.installDir.ToMakePath().String(), apexBundleName, fi.installDir)
160			fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", modulePath)
161			if a.primaryApexType && !symbolFilesNotNeeded {
162				fmt.Fprintln(w, "LOCAL_SOONG_SYMBOL_PATH :=", pathWhenActivated)
163			}
164			if len(fi.symlinks) > 0 {
165				fmt.Fprintln(w, "LOCAL_MODULE_SYMLINKS :=", strings.Join(fi.symlinks, " "))
166			}
167			newDataPaths := []android.DataPath{}
168			for _, path := range fi.dataPaths {
169				dataOutPath := modulePath + ":" + path.SrcPath.Rel()
170				if ok := seenDataOutPaths[dataOutPath]; !ok {
171					newDataPaths = append(newDataPaths, path)
172					seenDataOutPaths[dataOutPath] = true
173				}
174			}
175			if len(newDataPaths) > 0 {
176				fmt.Fprintln(w, "LOCAL_TEST_DATA :=", strings.Join(android.AndroidMkDataPaths(newDataPaths), " "))
177			}
178
179			if fi.module != nil && len(fi.module.NoticeFiles()) > 0 {
180				fmt.Fprintln(w, "LOCAL_NOTICE_FILE :=", strings.Join(fi.module.NoticeFiles().Strings(), " "))
181			}
182		} else {
183			modulePath = pathWhenActivated
184			fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", pathWhenActivated)
185
186			// For non-flattend APEXes, the merged notice file is attached to the APEX itself.
187			// We don't need to have notice file for the individual modules in it. Otherwise,
188			// we will have duplicated notice entries.
189			fmt.Fprintln(w, "LOCAL_NO_NOTICE_FILE := true")
190		}
191		fmt.Fprintln(w, "LOCAL_PREBUILT_MODULE_FILE :=", fi.builtFile.String())
192		fmt.Fprintln(w, "LOCAL_MODULE_CLASS :=", fi.class.nameInMake())
193		if fi.module != nil {
194			archStr := fi.module.Target().Arch.ArchType.String()
195			host := false
196			switch fi.module.Target().Os.Class {
197			case android.Host:
198				if fi.module.Target().HostCross {
199					if fi.module.Target().Arch.ArchType != android.Common {
200						fmt.Fprintln(w, "LOCAL_MODULE_HOST_CROSS_ARCH :=", archStr)
201					}
202				} else {
203					if fi.module.Target().Arch.ArchType != android.Common {
204						fmt.Fprintln(w, "LOCAL_MODULE_HOST_ARCH :=", archStr)
205					}
206				}
207				host = true
208			case android.Device:
209				if fi.module.Target().Arch.ArchType != android.Common {
210					fmt.Fprintln(w, "LOCAL_MODULE_TARGET_ARCH :=", archStr)
211				}
212			}
213			if host {
214				makeOs := fi.module.Target().Os.String()
215				if fi.module.Target().Os == android.Linux || fi.module.Target().Os == android.LinuxBionic {
216					makeOs = "linux"
217				}
218				fmt.Fprintln(w, "LOCAL_MODULE_HOST_OS :=", makeOs)
219				fmt.Fprintln(w, "LOCAL_IS_HOST_MODULE := true")
220			}
221		}
222		if fi.jacocoReportClassesFile != nil {
223			fmt.Fprintln(w, "LOCAL_SOONG_JACOCO_REPORT_CLASSES_JAR :=", fi.jacocoReportClassesFile.String())
224		}
225		switch fi.class {
226		case javaSharedLib:
227			// soong_java_prebuilt.mk sets LOCAL_MODULE_SUFFIX := .jar  Therefore
228			// we need to remove the suffix from LOCAL_MODULE_STEM, otherwise
229			// we will have foo.jar.jar
230			fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", strings.TrimSuffix(fi.stem(), ".jar"))
231			if javaModule, ok := fi.module.(java.ApexDependency); ok {
232				fmt.Fprintln(w, "LOCAL_SOONG_CLASSES_JAR :=", javaModule.ImplementationAndResourcesJars()[0].String())
233				fmt.Fprintln(w, "LOCAL_SOONG_HEADER_JAR :=", javaModule.HeaderJars()[0].String())
234			} else {
235				fmt.Fprintln(w, "LOCAL_SOONG_CLASSES_JAR :=", fi.builtFile.String())
236				fmt.Fprintln(w, "LOCAL_SOONG_HEADER_JAR :=", fi.builtFile.String())
237			}
238			fmt.Fprintln(w, "LOCAL_SOONG_DEX_JAR :=", fi.builtFile.String())
239			fmt.Fprintln(w, "LOCAL_DEX_PREOPT := false")
240			fmt.Fprintln(w, "include $(BUILD_SYSTEM)/soong_java_prebuilt.mk")
241		case app:
242			fmt.Fprintln(w, "LOCAL_CERTIFICATE :=", fi.certificate.AndroidMkString())
243			// soong_app_prebuilt.mk sets LOCAL_MODULE_SUFFIX := .apk  Therefore
244			// we need to remove the suffix from LOCAL_MODULE_STEM, otherwise
245			// we will have foo.apk.apk
246			fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", strings.TrimSuffix(fi.stem(), ".apk"))
247			if app, ok := fi.module.(*java.AndroidApp); ok {
248				if jniCoverageOutputs := app.JniCoverageOutputs(); len(jniCoverageOutputs) > 0 {
249					fmt.Fprintln(w, "LOCAL_PREBUILT_COVERAGE_ARCHIVE :=", strings.Join(jniCoverageOutputs.Strings(), " "))
250				}
251				if jniLibSymbols := app.JNISymbolsInstalls(modulePath); len(jniLibSymbols) > 0 {
252					fmt.Fprintln(w, "LOCAL_SOONG_JNI_LIBS_SYMBOLS :=", jniLibSymbols.String())
253				}
254			}
255			fmt.Fprintln(w, "include $(BUILD_SYSTEM)/soong_app_prebuilt.mk")
256		case appSet:
257			as, ok := fi.module.(*java.AndroidAppSet)
258			if !ok {
259				panic(fmt.Sprintf("Expected %s to be AndroidAppSet", fi.module))
260			}
261			fmt.Fprintln(w, "LOCAL_APK_SET_INSTALL_FILE :=", as.InstallFile())
262			fmt.Fprintln(w, "LOCAL_APKCERTS_FILE :=", as.APKCertsFile().String())
263			fmt.Fprintln(w, "include $(BUILD_SYSTEM)/soong_android_app_set.mk")
264		case nativeSharedLib, nativeExecutable, nativeTest:
265			fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", fi.stem())
266			if ccMod, ok := fi.module.(*cc.Module); ok {
267				if ccMod.UnstrippedOutputFile() != nil {
268					fmt.Fprintln(w, "LOCAL_SOONG_UNSTRIPPED_BINARY :=", ccMod.UnstrippedOutputFile().String())
269				}
270				ccMod.AndroidMkWriteAdditionalDependenciesForSourceAbiDiff(w)
271				if ccMod.CoverageOutputFile().Valid() {
272					fmt.Fprintln(w, "LOCAL_PREBUILT_COVERAGE_ARCHIVE :=", ccMod.CoverageOutputFile().String())
273				}
274			}
275			fmt.Fprintln(w, "include $(BUILD_SYSTEM)/soong_cc_prebuilt.mk")
276		default:
277			fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", fi.stem())
278			if fi.builtFile == a.manifestPbOut && apexType == flattenedApex {
279				if a.primaryApexType {
280					// To install companion files (init_rc, vintf_fragments)
281					// Copy some common properties of apexBundle to apex_manifest
282					commonProperties := []string{
283						"LOCAL_FULL_INIT_RC", "LOCAL_FULL_VINTF_FRAGMENTS",
284					}
285					for _, name := range commonProperties {
286						if value, ok := apexAndroidMkData.Entries.EntryMap[name]; ok {
287							fmt.Fprintln(w, name+" := "+strings.Join(value, " "))
288						}
289					}
290
291					// Make apex_manifest.pb module for this APEX to override all other
292					// modules in the APEXes being overridden by this APEX
293					var patterns []string
294					for _, o := range a.overridableProperties.Overrides {
295						patterns = append(patterns, "%."+o+a.suffix)
296					}
297					if len(patterns) > 0 {
298						fmt.Fprintln(w, "LOCAL_OVERRIDES_MODULES :=", strings.Join(patterns, " "))
299					}
300					if len(a.compatSymlinks) > 0 {
301						// For flattened apexes, compat symlinks are attached to apex_manifest.json which is guaranteed for every apex
302						postInstallCommands = append(postInstallCommands, a.compatSymlinks...)
303					}
304				}
305
306				// File_contexts of flattened APEXes should be merged into file_contexts.bin
307				fmt.Fprintln(w, "LOCAL_FILE_CONTEXTS :=", a.fileContexts)
308
309				if len(postInstallCommands) > 0 {
310					fmt.Fprintln(w, "LOCAL_POST_INSTALL_CMD :=", strings.Join(postInstallCommands, " && "))
311				}
312			}
313			fmt.Fprintln(w, "include $(BUILD_PREBUILT)")
314		}
315
316		// m <module_name> will build <module_name>.<apex_name> as well.
317		if fi.androidMkModuleName != moduleName && a.primaryApexType {
318			fmt.Fprintf(w, ".PHONY: %s\n", fi.androidMkModuleName)
319			fmt.Fprintf(w, "%s: %s\n", fi.androidMkModuleName, moduleName)
320		}
321	}
322	return moduleNames
323}
324
325func (a *apexBundle) writeRequiredModules(w io.Writer, apexBundleName string) {
326	var required []string
327	var targetRequired []string
328	var hostRequired []string
329	installMapSet := make(map[string]bool) // set of dependency module:location mappings
330	for _, fi := range a.filesInfo {
331		required = append(required, fi.requiredModuleNames...)
332		targetRequired = append(targetRequired, fi.targetRequiredModuleNames...)
333		hostRequired = append(hostRequired, fi.hostRequiredModuleNames...)
334		installMapSet[a.fullModuleName(apexBundleName, &fi)+":"+fi.installDir+"/"+fi.builtFile.Base()] = true
335	}
336
337	if len(required) > 0 {
338		fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES +=", strings.Join(required, " "))
339	}
340	if len(targetRequired) > 0 {
341		fmt.Fprintln(w, "LOCAL_TARGET_REQUIRED_MODULES +=", strings.Join(targetRequired, " "))
342	}
343	if len(hostRequired) > 0 {
344		fmt.Fprintln(w, "LOCAL_HOST_REQUIRED_MODULES +=", strings.Join(hostRequired, " "))
345	}
346	if len(installMapSet) > 0 {
347		var installs []string
348		installs = append(installs, android.SortedStringKeys(installMapSet)...)
349		fmt.Fprintln(w, "LOCAL_LICENSE_INSTALL_MAP +=", strings.Join(installs, " "))
350	}
351}
352
353func (a *apexBundle) androidMkForType() android.AndroidMkData {
354	return android.AndroidMkData{
355		Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
356			moduleNames := []string{}
357			apexType := a.properties.ApexType
358			if a.installable() {
359				apexName := proptools.StringDefault(a.properties.Apex_name, name)
360				moduleNames = a.androidMkForFiles(w, name, apexName, moduleDir, data)
361			}
362
363			if apexType == flattenedApex {
364				// Only image APEXes can be flattened.
365				fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)")
366				fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir)
367				fmt.Fprintln(w, "LOCAL_MODULE :=", name+a.suffix)
368				data.Entries.WriteLicenseVariables(w)
369				if len(moduleNames) > 0 {
370					fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES :=", strings.Join(moduleNames, " "))
371				}
372				a.writeRequiredModules(w, name)
373				fmt.Fprintln(w, "include $(BUILD_PHONY_PACKAGE)")
374
375			} else {
376				fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)")
377				fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir)
378				fmt.Fprintln(w, "LOCAL_MODULE :=", name+a.suffix)
379				data.Entries.WriteLicenseVariables(w)
380				fmt.Fprintln(w, "LOCAL_MODULE_CLASS := ETC") // do we need a new class?
381				fmt.Fprintln(w, "LOCAL_PREBUILT_MODULE_FILE :=", a.outputFile.String())
382				fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", a.installDir.ToMakePath().String())
383				stemSuffix := apexType.suffix()
384				if a.isCompressed {
385					stemSuffix = ".capex"
386				}
387				fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", name+stemSuffix)
388				fmt.Fprintln(w, "LOCAL_UNINSTALLABLE_MODULE :=", !a.installable())
389
390				// Because apex writes .mk with Custom(), we need to write manually some common properties
391				// which are available via data.Entries
392				commonProperties := []string{
393					"LOCAL_FULL_INIT_RC", "LOCAL_FULL_VINTF_FRAGMENTS",
394					"LOCAL_PROPRIETARY_MODULE", "LOCAL_VENDOR_MODULE", "LOCAL_ODM_MODULE", "LOCAL_PRODUCT_MODULE", "LOCAL_SYSTEM_EXT_MODULE",
395					"LOCAL_MODULE_OWNER",
396				}
397				for _, name := range commonProperties {
398					if value, ok := data.Entries.EntryMap[name]; ok {
399						fmt.Fprintln(w, name+" := "+strings.Join(value, " "))
400					}
401				}
402
403				if len(a.overridableProperties.Overrides) > 0 {
404					fmt.Fprintln(w, "LOCAL_OVERRIDES_MODULES :=", strings.Join(a.overridableProperties.Overrides, " "))
405				}
406				if len(moduleNames) > 0 {
407					fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES +=", strings.Join(moduleNames, " "))
408				}
409				if len(a.requiredDeps) > 0 {
410					fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES +=", strings.Join(a.requiredDeps, " "))
411				}
412				a.writeRequiredModules(w, name)
413				var postInstallCommands []string
414				if a.prebuiltFileToDelete != "" {
415					postInstallCommands = append(postInstallCommands, "rm -rf "+
416						filepath.Join(a.installDir.ToMakePath().String(), a.prebuiltFileToDelete))
417				}
418				// For unflattened apexes, compat symlinks are attached to apex package itself as LOCAL_POST_INSTALL_CMD
419				postInstallCommands = append(postInstallCommands, a.compatSymlinks...)
420				if len(postInstallCommands) > 0 {
421					fmt.Fprintln(w, "LOCAL_POST_INSTALL_CMD :=", strings.Join(postInstallCommands, " && "))
422				}
423
424				if a.mergedNotices.Merged.Valid() {
425					fmt.Fprintln(w, "LOCAL_NOTICE_FILE :=", a.mergedNotices.Merged.Path().String())
426				}
427
428				fmt.Fprintln(w, "include $(BUILD_PREBUILT)")
429
430				if apexType == imageApex {
431					fmt.Fprintln(w, "ALL_MODULES.$(my_register_name).BUNDLE :=", a.bundleModuleFile.String())
432				}
433				if len(a.lintReports) > 0 {
434					fmt.Fprintln(w, "ALL_MODULES.$(my_register_name).LINT_REPORTS :=",
435						strings.Join(a.lintReports.Strings(), " "))
436				}
437
438				if a.installedFilesFile != nil {
439					goal := "checkbuild"
440					distFile := name + "-installed-files.txt"
441					fmt.Fprintln(w, ".PHONY:", goal)
442					fmt.Fprintf(w, "$(call dist-for-goals,%s,%s:%s)\n",
443						goal, a.installedFilesFile.String(), distFile)
444				}
445				for _, dist := range data.Entries.GetDistForGoals(a) {
446					fmt.Fprintf(w, dist)
447				}
448
449				if a.apisUsedByModuleFile.String() != "" {
450					goal := "apps_only"
451					distFile := a.apisUsedByModuleFile.String()
452					fmt.Fprintf(w, "ifneq (,$(filter $(my_register_name),$(TARGET_BUILD_APPS)))\n"+
453						" $(call dist-for-goals,%s,%s:ndk_apis_usedby_apex/$(notdir %s))\n"+
454						"endif\n",
455						goal, distFile, distFile)
456				}
457
458				if a.apisBackedByModuleFile.String() != "" {
459					goal := "apps_only"
460					distFile := a.apisBackedByModuleFile.String()
461					fmt.Fprintf(w, "ifneq (,$(filter $(my_register_name),$(TARGET_BUILD_APPS)))\n"+
462						" $(call dist-for-goals,%s,%s:ndk_apis_backedby_apex/$(notdir %s))\n"+
463						"endif\n",
464						goal, distFile, distFile)
465				}
466			}
467		}}
468}
469