Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0e0cf1dc authored by Paul Duffin's avatar Paul Duffin
Browse files

Refactor sdk update mechanism

Creates a SnapshotBuilder and GeneratedSnapshotFile interfaces to allow
the java library snapshot work to be moved into the java package.

Test: m -j60 checkbuild
Change-Id: I857167616026149d5e85885621b53876b419ba9b
parent 263dcb73
Loading
Loading
Loading
Loading
+29 −0
Original line number Diff line number Diff line
@@ -31,6 +31,9 @@ type SdkAware interface {
	MemberName() string
	BuildWithSdks(sdks SdkRefs)
	RequiredSdks() SdkRefs

	// Build a snapshot of the module.
	BuildSnapshot(sdkModuleContext ModuleContext, builder SnapshotBuilder)
}

// SdkRef refers to a version of an SDK
@@ -103,6 +106,7 @@ type sdkProperties struct {
// interface. InitSdkAwareModule should be called to initialize this struct.
type SdkBase struct {
	properties sdkProperties
	module     SdkAware
}

func (s *SdkBase) sdkBase() *SdkBase {
@@ -142,9 +146,34 @@ func (s *SdkBase) RequiredSdks() SdkRefs {
	return s.properties.RequiredSdks
}

func (s *SdkBase) BuildSnapshot(sdkModuleContext ModuleContext, builder SnapshotBuilder) {
	sdkModuleContext.ModuleErrorf("module type " + sdkModuleContext.OtherModuleType(s.module) + " cannot be used in an sdk")
}

// InitSdkAwareModule initializes the SdkBase struct. This must be called by all modules including
// SdkBase.
func InitSdkAwareModule(m SdkAware) {
	base := m.sdkBase()
	base.module = m
	m.AddProperties(&base.properties)
}

// Provide support for generating the build rules which will build the snapshot.
type SnapshotBuilder interface {
	// Copy src to the dest (which is a snapshot relative path) and add the dest
	// to the zip
	CopyToSnapshot(src Path, dest string)

	// Get the AndroidBpFile for the snapshot.
	AndroidBpFile() GeneratedSnapshotFile

	// Get a versioned name appropriate for the SDK snapshot version being taken.
	VersionedSdkMemberName(unversionedName string) interface{}
}

// Provides support for generating a file, e.g. the Android.bp file.
type GeneratedSnapshotFile interface {
	Printfln(format string, args ...interface{})
	Indent()
	Dedent()
}
+51 −0
Original line number Diff line number Diff line
@@ -1664,6 +1664,57 @@ func (j *Library) DepsMutator(ctx android.BottomUpMutatorContext) {
	j.deps(ctx)
}

const (
	aidlIncludeDir     = "aidl"
	javaStubDir        = "java"
	javaStubFileSuffix = ".jar"
)

// path to the stub file of a java library. Relative to <sdk_root>/<api_dir>
func (j *Library) javaStubFilePathFor() string {
	return filepath.Join(javaStubDir, j.Name()+javaStubFileSuffix)
}

func (j *Library) BuildSnapshot(sdkModuleContext android.ModuleContext, builder android.SnapshotBuilder) {
	headerJars := j.HeaderJars()
	if len(headerJars) != 1 {
		panic(fmt.Errorf("there must be only one header jar from %q", j.Name()))
	}
	snapshotRelativeJavaLibPath := j.javaStubFilePathFor()
	builder.CopyToSnapshot(headerJars[0], snapshotRelativeJavaLibPath)

	for _, dir := range j.AidlIncludeDirs() {
		// TODO(jiyong): copy parcelable declarations only
		aidlFiles, _ := sdkModuleContext.GlobWithDeps(dir.String()+"/**/*.aidl", nil)
		for _, file := range aidlFiles {
			builder.CopyToSnapshot(android.PathForSource(sdkModuleContext, file), filepath.Join(aidlIncludeDir, file))
		}
	}

	name := j.Name()
	bp := builder.AndroidBpFile()
	bp.Printfln("java_import {")
	bp.Indent()
	bp.Printfln("name: %q,", builder.VersionedSdkMemberName(name))
	bp.Printfln("sdk_member_name: %q,", name)
	bp.Printfln("jars: [%q],", snapshotRelativeJavaLibPath)
	bp.Dedent()
	bp.Printfln("}")
	bp.Printfln("")

	// This module is for the case when the source tree for the unversioned module
	// doesn't exist (i.e. building in an unbundled tree). "prefer:" is set to false
	// so that this module does not eclipse the unversioned module if it exists.
	bp.Printfln("java_import {")
	bp.Indent()
	bp.Printfln("name: %q,", name)
	bp.Printfln("jars: [%q],", snapshotRelativeJavaLibPath)
	bp.Printfln("prefer: false,")
	bp.Dedent()
	bp.Printfln("}")
	bp.Printfln("")
}

// java_library builds and links sources into a `.jar` file for the device, and possibly for the host as well.
//
// By default, a java_library has a single variant that produces a `.jar` file containing `.class` files that were
+171 −201
Original line number Diff line number Diff line
@@ -43,17 +43,17 @@ func newGeneratedFile(ctx android.ModuleContext, path ...string) *generatedFile
	}
}

func (gf *generatedFile) indent() {
func (gf *generatedFile) Indent() {
	gf.indentLevel++
}

func (gf *generatedFile) dedent() {
func (gf *generatedFile) Dedent() {
	gf.indentLevel--
}

func (gf *generatedFile) printfln(format string, args ...interface{}) {
func (gf *generatedFile) Printfln(format string, args ...interface{}) {
	// ninja consumes newline characters in rspfile_content. Prevent it by
	// escaping the backslash in the newline character. The extra backshash
	// escaping the backslash in the newline character. The extra backslash
	// is removed when the rspfile is written to the actual script file
	fmt.Fprintf(&(gf.content), strings.Repeat("    ", gf.indentLevel)+format+"\\n", args...)
}
@@ -70,8 +70,8 @@ func (gf *generatedFile) build(pctx android.PackageContext, ctx android.BuilderC
	rb.Build(pctx, ctx, gf.path.Base(), "Build "+gf.path.Base())
}

func (s *sdk) javaLibs(ctx android.ModuleContext) []*java.Library {
	result := []*java.Library{}
func (s *sdk) javaLibs(ctx android.ModuleContext) []android.SdkAware {
	result := []android.SdkAware{}
	ctx.VisitDirectDeps(func(m android.Module) {
		if j, ok := m.(*java.Library); ok {
			result = append(result, j)
@@ -180,20 +180,12 @@ func (s *sdk) nativeMemberInfos(ctx android.ModuleContext) []*nativeLibInfo {
//            libFoo.so   : a stub library

const (
	aidlIncludeDir            = "aidl"
	javaStubDir               = "java"
	javaStubFileSuffix        = ".jar"
	nativeIncludeDir          = "include"
	nativeGeneratedIncludeDir = "include_gen"
	nativeStubDir             = "lib"
	nativeStubFileSuffix      = ".so"
)

// path to the stub file of a java library. Relative to <sdk_root>/<api_dir>
func javaStubFilePathFor(javaLib *java.Library) string {
	return filepath.Join(javaStubDir, javaLib.Name()+javaStubFileSuffix)
}

// path to the stub file of a native shared library. Relative to <sdk_root>/<api_dir>
func nativeStubFilePathFor(lib archSpecificNativeLibInfo) string {
	return filepath.Join(lib.archType,
@@ -231,163 +223,80 @@ func versionedSdkMemberName(ctx android.ModuleContext, memberName string, versio
	return ctx.ModuleName() + "_" + memberName + string(android.SdkVersionSeparator) + version
}

// buildAndroidBp creates the blueprint file that defines prebuilt modules for each of
// the SDK members, and the entire sdk_snapshot module for the specified version
// TODO(jiyong): create a meta info file (e.g. json, protobuf, etc.) instead, and convert it to
// Android.bp in the (presumably old) branch where the snapshots will be used. This will give us
// some flexibility to introduce backwards incompatible changes in soong.
func (s *sdk) buildAndroidBp(ctx android.ModuleContext, version string) android.OutputPath {
	bp := newGeneratedFile(ctx, "snapshot", "Android.bp")
	bp.printfln("// This is auto-generated. DO NOT EDIT.")
	bp.printfln("")

	javaLibModules := s.javaLibs(ctx)
	for _, m := range javaLibModules {
		name := m.Name()
		bp.printfln("java_import {")
		bp.indent()
		bp.printfln("name: %q,", versionedSdkMemberName(ctx, name, version))
		bp.printfln("sdk_member_name: %q,", name)
		bp.printfln("jars: [%q],", javaStubFilePathFor(m))
		bp.dedent()
		bp.printfln("}")
		bp.printfln("")

		// This module is for the case when the source tree for the unversioned module
		// doesn't exist (i.e. building in an unbundled tree). "prefer:" is set to false
		// so that this module does not eclipse the unversioned module if it exists.
		bp.printfln("java_import {")
		bp.indent()
		bp.printfln("name: %q,", name)
		bp.printfln("jars: [%q],", javaStubFilePathFor(m))
		bp.printfln("prefer: false,")
		bp.dedent()
		bp.printfln("}")
		bp.printfln("")
	}
// buildSnapshot is the main function in this source file. It creates rules to copy
// the contents (header files, stub libraries, etc) into the zip file.
func (s *sdk) buildSnapshot(ctx android.ModuleContext) android.OutputPath {
	snapshotDir := android.PathForModuleOut(ctx, "snapshot")

	nativeLibInfos := s.nativeMemberInfos(ctx)
	for _, info := range nativeLibInfos {
		bp.printfln("cc_prebuilt_library_shared {")
		bp.indent()
		bp.printfln("name: %q,", versionedSdkMemberName(ctx, info.name, version))
		bp.printfln("sdk_member_name: %q,", info.name)
	bp := newGeneratedFile(ctx, "snapshot", "Android.bp")
	bp.Printfln("// This is auto-generated. DO NOT EDIT.")
	bp.Printfln("")

		// a function for emitting include dirs
		printExportedDirsForNativeLibs := func(lib archSpecificNativeLibInfo, systemInclude bool) {
			includeDirs := nativeIncludeDirPathsFor(ctx, lib, systemInclude, info.hasArchSpecificFlags)
			if len(includeDirs) == 0 {
				return
			}
			if !systemInclude {
				bp.printfln("export_include_dirs: [")
			} else {
				bp.printfln("export_system_include_dirs: [")
			}
			bp.indent()
			for _, dir := range includeDirs {
				bp.printfln("%q,", dir)
			}
			bp.dedent()
			bp.printfln("],")
	builder := &snapshotBuilder{
		ctx:           ctx,
		version:       "current",
		snapshotDir:   snapshotDir.OutputPath,
		androidBpFile: bp,
		filesToZip:    []android.Path{bp.path},
	}

		if !info.hasArchSpecificFlags {
			printExportedDirsForNativeLibs(info.archVariants[0], false /*systemInclude*/)
			printExportedDirsForNativeLibs(info.archVariants[0], true /*systemInclude*/)
	// copy exported AIDL files and stub jar files
	javaLibs := s.javaLibs(ctx)
	for _, m := range javaLibs {
		m.BuildSnapshot(ctx, builder)
	}

		bp.printfln("arch: {")
		bp.indent()
		for _, av := range info.archVariants {
			bp.printfln("%s: {", av.archType)
			bp.indent()
			bp.printfln("srcs: [%q],", nativeStubFilePathFor(av))
			if info.hasArchSpecificFlags {
				// export_* properties are added inside the arch: {<arch>: {...}} block
				printExportedDirsForNativeLibs(av, false /*systemInclude*/)
				printExportedDirsForNativeLibs(av, true /*systemInclude*/)
			}
			bp.dedent()
			bp.printfln("},") // <arch>
		}
		bp.dedent()
		bp.printfln("},") // arch
		bp.printfln("stl: \"none\",")
		bp.printfln("system_shared_libs: [],")
		bp.dedent()
		bp.printfln("}") // cc_prebuilt_library_shared
		bp.printfln("")
	// copy exported header files and stub *.so files
	nativeLibInfos := s.nativeMemberInfos(ctx)
	for _, info := range nativeLibInfos {
		buildSharedNativeLibSnapshot(ctx, info, builder)
	}

	bp.printfln("sdk_snapshot {")
	bp.indent()
	bp.printfln("name: %q,", ctx.ModuleName()+string(android.SdkVersionSeparator)+version)
	if len(javaLibModules) > 0 {
		bp.printfln("java_libs: [")
		bp.indent()
		for _, m := range javaLibModules {
			bp.printfln("%q,", versionedSdkMemberName(ctx, m.Name(), version))
	// generate Android.bp

	bp.Printfln("sdk_snapshot {")
	bp.Indent()
	bp.Printfln("name: %q,", ctx.ModuleName()+string(android.SdkVersionSeparator)+builder.version)
	if len(javaLibs) > 0 {
		bp.Printfln("java_libs: [")
		bp.Indent()
		for _, m := range javaLibs {
			bp.Printfln("%q,", builder.VersionedSdkMemberName(m.Name()))
		}
		bp.dedent()
		bp.printfln("],") // java_libs
		bp.Dedent()
		bp.Printfln("],") // java_libs
	}
	if len(nativeLibInfos) > 0 {
		bp.printfln("native_shared_libs: [")
		bp.indent()
		bp.Printfln("native_shared_libs: [")
		bp.Indent()
		for _, info := range nativeLibInfos {
			bp.printfln("%q,", versionedSdkMemberName(ctx, info.name, version))
			bp.Printfln("%q,", builder.VersionedSdkMemberName(info.name))
		}
		bp.dedent()
		bp.printfln("],") // native_shared_libs
		bp.Dedent()
		bp.Printfln("],") // native_shared_libs
	}
	bp.dedent()
	bp.printfln("}") // sdk_snapshot
	bp.printfln("")
	bp.Dedent()
	bp.Printfln("}") // sdk_snapshot
	bp.Printfln("")

	bp.build(pctx, ctx, nil)
	return bp.path
}

// buildSnapshot is the main function in this source file. It creates rules to copy
// the contents (header files, stub libraries, etc) into the zip file.
func (s *sdk) buildSnapshot(ctx android.ModuleContext) android.OutputPath {
	snapshotPath := func(paths ...string) android.OutputPath {
		return android.PathForModuleOut(ctx, "snapshot").Join(ctx, paths...)
	}
	filesToZip := builder.filesToZip

	var filesToZip android.Paths
	// copy src to dest and add the dest to the zip
	copy := func(src android.Path, dest android.OutputPath) {
		ctx.Build(pctx, android.BuildParams{
			Rule:   android.Cp,
			Input:  src,
			Output: dest,
		})
		filesToZip = append(filesToZip, dest)
	}

	// copy exported AIDL files and stub jar files
	for _, m := range s.javaLibs(ctx) {
		headerJars := m.HeaderJars()
		if len(headerJars) != 1 {
			panic(fmt.Errorf("there must be only one header jar from %q", m.Name()))
		}
		copy(headerJars[0], snapshotPath(javaStubFilePathFor(m)))
	// zip them all
	zipFile := android.PathForModuleOut(ctx, ctx.ModuleName()+"-current.zip").OutputPath
	rb := android.NewRuleBuilder()
	rb.Command().
		BuiltTool(ctx, "soong_zip").
		FlagWithArg("-C ", builder.snapshotDir.String()).
		FlagWithRspFileInputList("-l ", filesToZip).
		FlagWithOutput("-o ", zipFile)
	rb.Build(pctx, ctx, "snapshot", "Building snapshot for "+ctx.ModuleName())

		for _, dir := range m.AidlIncludeDirs() {
			// TODO(jiyong): copy parcelable declarations only
			aidlFiles, _ := ctx.GlobWithDeps(dir.String()+"/**/*.aidl", nil)
			for _, file := range aidlFiles {
				copy(android.PathForSource(ctx, file), snapshotPath(aidlIncludeDir, file))
			}
		}
	return zipFile
}

	// copy exported header files and stub *.so files
	nativeLibInfos := s.nativeMemberInfos(ctx)
	for _, info := range nativeLibInfos {

func buildSharedNativeLibSnapshot(ctx android.ModuleContext, info *nativeLibInfo, builder android.SnapshotBuilder) {
	// a function for emitting include dirs
	printExportedDirCopyCommandsForNativeLibs := func(lib archSpecificNativeLibInfo) {
		includeDirs := lib.exportedIncludeDirs
@@ -409,8 +318,8 @@ func (s *sdk) buildSnapshot(ctx android.ModuleContext) android.OutputPath {
			headers, _ := ctx.GlobWithDeps(dir.String()+"/**/*.h", nil)
			for _, file := range headers {
				src := android.PathForSource(ctx, file)
					dest := snapshotPath(targetDir, file)
					copy(src, dest)
				dest := filepath.Join(targetDir, file)
				builder.CopyToSnapshot(src, dest)
			}
		}

@@ -420,8 +329,8 @@ func (s *sdk) buildSnapshot(ctx android.ModuleContext) android.OutputPath {
			if info.hasArchSpecificFlags {
				targetDir = filepath.Join(lib.archType, targetDir)
			}
				dest := snapshotPath(targetDir, lib.name, file.Rel())
				copy(file, dest)
			dest := filepath.Join(targetDir, lib.name, file.Rel())
			builder.CopyToSnapshot(file, dest)
		}
	}

@@ -431,27 +340,88 @@ func (s *sdk) buildSnapshot(ctx android.ModuleContext) android.OutputPath {

	// for each architecture
	for _, av := range info.archVariants {
			copy(av.outputFile, snapshotPath(nativeStubFilePathFor(av)))
		builder.CopyToSnapshot(av.outputFile, nativeStubFilePathFor(av))

		if info.hasArchSpecificFlags {
			printExportedDirCopyCommandsForNativeLibs(av)
		}
	}

	bp := builder.AndroidBpFile()
	bp.Printfln("cc_prebuilt_library_shared {")
	bp.Indent()
	bp.Printfln("name: %q,", builder.VersionedSdkMemberName(info.name))
	bp.Printfln("sdk_member_name: %q,", info.name)

	// a function for emitting include dirs
	printExportedDirsForNativeLibs := func(lib archSpecificNativeLibInfo, systemInclude bool) {
		includeDirs := nativeIncludeDirPathsFor(ctx, lib, systemInclude, info.hasArchSpecificFlags)
		if len(includeDirs) == 0 {
			return
		}
		if !systemInclude {
			bp.Printfln("export_include_dirs: [")
		} else {
			bp.Printfln("export_system_include_dirs: [")
		}
		bp.Indent()
		for _, dir := range includeDirs {
			bp.Printfln("%q,", dir)
		}
		bp.Dedent()
		bp.Printfln("],")
	}

	// generate Android.bp
	bp := s.buildAndroidBp(ctx, "current")
	filesToZip = append(filesToZip, bp)
	if !info.hasArchSpecificFlags {
		printExportedDirsForNativeLibs(info.archVariants[0], false /*systemInclude*/)
		printExportedDirsForNativeLibs(info.archVariants[0], true /*systemInclude*/)
	}

	// zip them all
	zipFile := android.PathForModuleOut(ctx, ctx.ModuleName()+"-current.zip").OutputPath
	rb := android.NewRuleBuilder()
	rb.Command().
		BuiltTool(ctx, "soong_zip").
		FlagWithArg("-C ", snapshotPath().String()).
		FlagWithRspFileInputList("-l ", filesToZip).
		FlagWithOutput("-o ", zipFile)
	rb.Build(pctx, ctx, "snapshot", "Building snapshot for "+ctx.ModuleName())
	bp.Printfln("arch: {")
	bp.Indent()
	for _, av := range info.archVariants {
		bp.Printfln("%s: {", av.archType)
		bp.Indent()
		bp.Printfln("srcs: [%q],", nativeStubFilePathFor(av))
		if info.hasArchSpecificFlags {
			// export_* properties are added inside the arch: {<arch>: {...}} block
			printExportedDirsForNativeLibs(av, false /*systemInclude*/)
			printExportedDirsForNativeLibs(av, true /*systemInclude*/)
		}
		bp.Dedent()
		bp.Printfln("},") // <arch>
	}
	bp.Dedent()
	bp.Printfln("},") // arch
	bp.Printfln("stl: \"none\",")
	bp.Printfln("system_shared_libs: [],")
	bp.Dedent()
	bp.Printfln("}") // cc_prebuilt_library_shared
	bp.Printfln("")
}

	return zipFile
type snapshotBuilder struct {
	ctx           android.ModuleContext
	version       string
	snapshotDir   android.OutputPath
	filesToZip    android.Paths
	androidBpFile *generatedFile
}

func (s *snapshotBuilder) CopyToSnapshot(src android.Path, dest string) {
	path := s.snapshotDir.Join(s.ctx, dest)
	s.ctx.Build(pctx, android.BuildParams{
		Rule:   android.Cp,
		Input:  src,
		Output: path,
	})
	s.filesToZip = append(s.filesToZip, path)
}

func (s *snapshotBuilder) AndroidBpFile() android.GeneratedSnapshotFile {
	return s.androidBpFile
}

func (s *snapshotBuilder) VersionedSdkMemberName(unversionedName string) interface{} {
	return versionedSdkMemberName(s.ctx, unversionedName, s.version)
}