1// Copyright 2019 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 cc
16
17import (
18	"path/filepath"
19
20	"android/soong/android"
21
22	"github.com/google/blueprint"
23	"github.com/google/blueprint/proptools"
24)
25
26// This file contains support for using cc library modules within an sdk.
27
28var sharedLibrarySdkMemberType = &librarySdkMemberType{
29	SdkMemberTypeBase: android.SdkMemberTypeBase{
30		PropertyName: "native_shared_libs",
31		SupportsSdk:  true,
32	},
33	prebuiltModuleType: "cc_prebuilt_library_shared",
34	linkTypes:          []string{"shared"},
35}
36
37var staticLibrarySdkMemberType = &librarySdkMemberType{
38	SdkMemberTypeBase: android.SdkMemberTypeBase{
39		PropertyName: "native_static_libs",
40		SupportsSdk:  true,
41	},
42	prebuiltModuleType: "cc_prebuilt_library_static",
43	linkTypes:          []string{"static"},
44}
45
46var staticAndSharedLibrarySdkMemberType = &librarySdkMemberType{
47	SdkMemberTypeBase: android.SdkMemberTypeBase{
48		PropertyName: "native_libs",
49		SupportsSdk:  true,
50	},
51	prebuiltModuleType: "cc_prebuilt_library",
52	linkTypes:          []string{"static", "shared"},
53}
54
55func init() {
56	// Register sdk member types.
57	android.RegisterSdkMemberType(sharedLibrarySdkMemberType)
58	android.RegisterSdkMemberType(staticLibrarySdkMemberType)
59	android.RegisterSdkMemberType(staticAndSharedLibrarySdkMemberType)
60}
61
62type librarySdkMemberType struct {
63	android.SdkMemberTypeBase
64
65	prebuiltModuleType string
66
67	noOutputFiles bool // True if there are no srcs files.
68
69	// The set of link types supported. A set of "static", "shared", or nil to
70	// skip link type variations.
71	linkTypes []string
72}
73
74func (mt *librarySdkMemberType) AddDependencies(mctx android.BottomUpMutatorContext, dependencyTag blueprint.DependencyTag, names []string) {
75	targets := mctx.MultiTargets()
76	for _, lib := range names {
77		for _, target := range targets {
78			name, version := StubsLibNameAndVersion(lib)
79			if version == "" {
80				version = LatestStubsVersionFor(mctx.Config(), name)
81			}
82			if mt.linkTypes == nil {
83				mctx.AddFarVariationDependencies(append(target.Variations(), []blueprint.Variation{
84					{Mutator: "image", Variation: android.CoreVariation},
85					{Mutator: "version", Variation: version},
86				}...), dependencyTag, name)
87			} else {
88				for _, linkType := range mt.linkTypes {
89					mctx.AddFarVariationDependencies(append(target.Variations(), []blueprint.Variation{
90						{Mutator: "image", Variation: android.CoreVariation},
91						{Mutator: "link", Variation: linkType},
92						{Mutator: "version", Variation: version},
93					}...), dependencyTag, name)
94				}
95			}
96		}
97	}
98}
99
100func (mt *librarySdkMemberType) IsInstance(module android.Module) bool {
101	// Check the module to see if it can be used with this module type.
102	if m, ok := module.(*Module); ok {
103		for _, allowableMemberType := range m.sdkMemberTypes {
104			if allowableMemberType == mt {
105				return true
106			}
107		}
108	}
109
110	return false
111}
112
113func (mt *librarySdkMemberType) AddPrebuiltModule(ctx android.SdkMemberContext, member android.SdkMember) android.BpModule {
114	pbm := ctx.SnapshotBuilder().AddPrebuiltModule(member, mt.prebuiltModuleType)
115
116	ccModule := member.Variants()[0].(*Module)
117
118	sdkVersion := ccModule.SdkVersion()
119	if sdkVersion != "" {
120		pbm.AddProperty("sdk_version", sdkVersion)
121	}
122
123	stl := ccModule.stl.Properties.Stl
124	if stl != nil {
125		pbm.AddProperty("stl", proptools.String(stl))
126	}
127
128	if lib, ok := ccModule.linker.(*libraryDecorator); ok {
129		uhs := lib.Properties.Unique_host_soname
130		if uhs != nil {
131			pbm.AddProperty("unique_host_soname", proptools.Bool(uhs))
132		}
133	}
134
135	return pbm
136}
137
138func (mt *librarySdkMemberType) CreateVariantPropertiesStruct() android.SdkMemberProperties {
139	return &nativeLibInfoProperties{memberType: mt}
140}
141
142func isGeneratedHeaderDirectory(p android.Path) bool {
143	_, gen := p.(android.WritablePath)
144	return gen
145}
146
147type includeDirsProperty struct {
148	// Accessor to retrieve the paths
149	pathsGetter func(libInfo *nativeLibInfoProperties) android.Paths
150
151	// The name of the property in the prebuilt library, "" means there is no property.
152	propertyName string
153
154	// The directory within the snapshot directory into which items should be copied.
155	snapshotDir string
156
157	// True if the items on the path should be copied.
158	copy bool
159
160	// True if the paths represent directories, files if they represent files.
161	dirs bool
162}
163
164var includeDirProperties = []includeDirsProperty{
165	{
166		// ExportedIncludeDirs lists directories that contains some header files to be
167		// copied into a directory in the snapshot. The snapshot directories must be added to
168		// the export_include_dirs property in the prebuilt module in the snapshot.
169		pathsGetter:  func(libInfo *nativeLibInfoProperties) android.Paths { return libInfo.ExportedIncludeDirs },
170		propertyName: "export_include_dirs",
171		snapshotDir:  nativeIncludeDir,
172		copy:         true,
173		dirs:         true,
174	},
175	{
176		// ExportedSystemIncludeDirs lists directories that contains some system header files to
177		// be copied into a directory in the snapshot. The snapshot directories must be added to
178		// the export_system_include_dirs property in the prebuilt module in the snapshot.
179		pathsGetter:  func(libInfo *nativeLibInfoProperties) android.Paths { return libInfo.ExportedSystemIncludeDirs },
180		propertyName: "export_system_include_dirs",
181		snapshotDir:  nativeIncludeDir,
182		copy:         true,
183		dirs:         true,
184	},
185	{
186		// exportedGeneratedIncludeDirs lists directories that contains some header files
187		// that are explicitly listed in the exportedGeneratedHeaders property. So, the contents
188		// of these directories do not need to be copied, but these directories do need adding to
189		// the export_include_dirs property in the prebuilt module in the snapshot.
190		pathsGetter:  func(libInfo *nativeLibInfoProperties) android.Paths { return libInfo.exportedGeneratedIncludeDirs },
191		propertyName: "export_include_dirs",
192		snapshotDir:  nativeGeneratedIncludeDir,
193		copy:         false,
194		dirs:         true,
195	},
196	{
197		// exportedGeneratedHeaders lists header files that are in one of the directories
198		// specified in exportedGeneratedIncludeDirs must be copied into the snapshot.
199		// As they are in a directory in exportedGeneratedIncludeDirs they do not need adding to a
200		// property in the prebuilt module in the snapshot.
201		pathsGetter:  func(libInfo *nativeLibInfoProperties) android.Paths { return libInfo.exportedGeneratedHeaders },
202		propertyName: "",
203		snapshotDir:  nativeGeneratedIncludeDir,
204		copy:         true,
205		dirs:         false,
206	},
207}
208
209// Add properties that may, or may not, be arch specific.
210func addPossiblyArchSpecificProperties(sdkModuleContext android.ModuleContext, builder android.SnapshotBuilder, libInfo *nativeLibInfoProperties, outputProperties android.BpPropertySet) {
211
212	// Copy the generated library to the snapshot and add a reference to it in the .bp module.
213	if libInfo.outputFile != nil {
214		nativeLibraryPath := nativeLibraryPathFor(libInfo)
215		builder.CopyToSnapshot(libInfo.outputFile, nativeLibraryPath)
216		outputProperties.AddProperty("srcs", []string{nativeLibraryPath})
217	}
218
219	if len(libInfo.SharedLibs) > 0 {
220		outputProperties.AddPropertyWithTag("shared_libs", libInfo.SharedLibs, builder.SdkMemberReferencePropertyTag(false))
221	}
222
223	// SystemSharedLibs needs to be propagated if it's a list, even if it's empty,
224	// so check for non-nil instead of nonzero length.
225	if libInfo.SystemSharedLibs != nil {
226		outputProperties.AddPropertyWithTag("system_shared_libs", libInfo.SystemSharedLibs, builder.SdkMemberReferencePropertyTag(false))
227	}
228
229	// Map from property name to the include dirs to add to the prebuilt module in the snapshot.
230	includeDirs := make(map[string][]string)
231
232	// Iterate over each include directory property, copying files and collating property
233	// values where necessary.
234	for _, propertyInfo := range includeDirProperties {
235		// Calculate the base directory in the snapshot into which the files will be copied.
236		// lib.ArchType is "" for common properties.
237		targetDir := filepath.Join(libInfo.OsPrefix(), libInfo.archType, propertyInfo.snapshotDir)
238
239		propertyName := propertyInfo.propertyName
240
241		// Iterate over each path in one of the include directory properties.
242		for _, path := range propertyInfo.pathsGetter(libInfo) {
243
244			// Copy the files/directories when necessary.
245			if propertyInfo.copy {
246				if propertyInfo.dirs {
247					// When copying a directory glob and copy all the headers within it.
248					// TODO(jiyong) copy headers having other suffixes
249					headers, _ := sdkModuleContext.GlobWithDeps(path.String()+"/**/*.h", nil)
250					for _, file := range headers {
251						src := android.PathForSource(sdkModuleContext, file)
252						dest := filepath.Join(targetDir, file)
253						builder.CopyToSnapshot(src, dest)
254					}
255				} else {
256					// Otherwise, just copy the files.
257					dest := filepath.Join(targetDir, libInfo.name, path.Rel())
258					builder.CopyToSnapshot(path, dest)
259				}
260			}
261
262			// Only directories are added to a property.
263			if propertyInfo.dirs {
264				var snapshotPath string
265				if isGeneratedHeaderDirectory(path) {
266					snapshotPath = filepath.Join(targetDir, libInfo.name)
267				} else {
268					snapshotPath = filepath.Join(targetDir, path.String())
269				}
270
271				includeDirs[propertyName] = append(includeDirs[propertyName], snapshotPath)
272			}
273		}
274	}
275
276	// Add the collated include dir properties to the output.
277	for property, dirs := range includeDirs {
278		outputProperties.AddProperty(property, dirs)
279	}
280
281	if len(libInfo.StubsVersion) > 0 {
282		stubsSet := outputProperties.AddPropertySet("stubs")
283		stubsSet.AddProperty("versions", []string{libInfo.StubsVersion})
284	}
285}
286
287const (
288	nativeIncludeDir          = "include"
289	nativeGeneratedIncludeDir = "include_gen"
290	nativeStubDir             = "lib"
291)
292
293// path to the native library. Relative to <sdk_root>/<api_dir>
294func nativeLibraryPathFor(lib *nativeLibInfoProperties) string {
295	return filepath.Join(lib.OsPrefix(), lib.archType,
296		nativeStubDir, lib.outputFile.Base())
297}
298
299// nativeLibInfoProperties represents properties of a native lib
300//
301// The exported (capitalized) fields will be examined and may be changed during common value extraction.
302// The unexported fields will be left untouched.
303type nativeLibInfoProperties struct {
304	android.SdkMemberPropertiesBase
305
306	memberType *librarySdkMemberType
307
308	// The name of the library, is not exported as this must not be changed during optimization.
309	name string
310
311	// archType is not exported as if set (to a non default value) it is always arch specific.
312	// This is "" for common properties.
313	archType string
314
315	// The list of possibly common exported include dirs.
316	//
317	// This field is exported as its contents may not be arch specific.
318	ExportedIncludeDirs android.Paths `android:"arch_variant"`
319
320	// The list of arch specific exported generated include dirs.
321	//
322	// This field is not exported as its contents are always arch specific.
323	exportedGeneratedIncludeDirs android.Paths
324
325	// The list of arch specific exported generated header files.
326	//
327	// This field is not exported as its contents are is always arch specific.
328	exportedGeneratedHeaders android.Paths
329
330	// The list of possibly common exported system include dirs.
331	//
332	// This field is exported as its contents may not be arch specific.
333	ExportedSystemIncludeDirs android.Paths `android:"arch_variant"`
334
335	// The list of possibly common exported flags.
336	//
337	// This field is exported as its contents may not be arch specific.
338	ExportedFlags []string `android:"arch_variant"`
339
340	// The set of shared libraries
341	//
342	// This field is exported as its contents may not be arch specific.
343	SharedLibs []string `android:"arch_variant"`
344
345	// The set of system shared libraries. Note nil and [] are semantically
346	// distinct - see BaseLinkerProperties.System_shared_libs.
347	//
348	// This field is exported as its contents may not be arch specific.
349	SystemSharedLibs []string `android:"arch_variant"`
350
351	// The specific stubs version for the lib variant, or empty string if stubs
352	// are not in use.
353	//
354	// Marked 'ignored-on-host' as the StubsVersion() from which this is initialized is
355	// not set on host and the stubs.versions property which this is written to is does
356	// not vary by arch so cannot be android specific.
357	StubsVersion string `sdk:"ignored-on-host"`
358
359	// outputFile is not exported as it is always arch specific.
360	outputFile android.Path
361}
362
363func (p *nativeLibInfoProperties) PopulateFromVariant(ctx android.SdkMemberContext, variant android.Module) {
364	ccModule := variant.(*Module)
365
366	// If the library has some link types then it produces an output binary file, otherwise it
367	// is header only.
368	if !p.memberType.noOutputFiles {
369		p.outputFile = getRequiredMemberOutputFile(ctx, ccModule)
370	}
371
372	// Separate out the generated include dirs (which are arch specific) from the
373	// include dirs (which may not be).
374	exportedIncludeDirs, exportedGeneratedIncludeDirs := android.FilterPathListPredicate(
375		ccModule.ExportedIncludeDirs(), isGeneratedHeaderDirectory)
376
377	p.name = variant.Name()
378	p.archType = ccModule.Target().Arch.ArchType.String()
379
380	// Make sure that the include directories are unique.
381	p.ExportedIncludeDirs = android.FirstUniquePaths(exportedIncludeDirs)
382	p.exportedGeneratedIncludeDirs = android.FirstUniquePaths(exportedGeneratedIncludeDirs)
383
384	// Take a copy before filtering out duplicates to avoid changing the slice owned by the
385	// ccModule.
386	dirs := append(android.Paths(nil), ccModule.ExportedSystemIncludeDirs()...)
387	p.ExportedSystemIncludeDirs = android.FirstUniquePaths(dirs)
388
389	p.ExportedFlags = ccModule.ExportedFlags()
390	if ccModule.linker != nil {
391		specifiedDeps := specifiedDeps{}
392		specifiedDeps = ccModule.linker.linkerSpecifiedDeps(specifiedDeps)
393
394		if !ccModule.HasStubsVariants() {
395			// Propagate dynamic dependencies for implementation libs, but not stubs.
396			p.SharedLibs = specifiedDeps.sharedLibs
397		}
398		p.SystemSharedLibs = specifiedDeps.systemSharedLibs
399	}
400	p.exportedGeneratedHeaders = ccModule.ExportedGeneratedHeaders()
401
402	if ccModule.HasStubsVariants() {
403		p.StubsVersion = ccModule.StubsVersion()
404	}
405}
406
407func getRequiredMemberOutputFile(ctx android.SdkMemberContext, ccModule *Module) android.Path {
408	var path android.Path
409	outputFile := ccModule.OutputFile()
410	if outputFile.Valid() {
411		path = outputFile.Path()
412	} else {
413		ctx.SdkModuleContext().ModuleErrorf("member variant %s does not have a valid output file", ccModule)
414	}
415	return path
416}
417
418func (p *nativeLibInfoProperties) AddToPropertySet(ctx android.SdkMemberContext, propertySet android.BpPropertySet) {
419	addPossiblyArchSpecificProperties(ctx.SdkModuleContext(), ctx.SnapshotBuilder(), p, propertySet)
420}
421