blob: 2004c9290a95553b71d0fb5ead2ea5ba2facf179 [file] [log] [blame]
// Copyright 2021 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package android
import (
"fmt"
"strconv"
"strings"
)
type SdkContext interface {
// SdkVersion returns SdkSpec that corresponds to the sdk_version property of the current module
SdkVersion(ctx EarlyModuleContext) SdkSpec
// SystemModules returns the system_modules property of the current module, or an empty string if it is not set.
SystemModules() string
// MinSdkVersion returns SdkSpec that corresponds to the min_sdk_version property of the current module,
// or from sdk_version if it is not set.
MinSdkVersion(ctx EarlyModuleContext) SdkSpec
// TargetSdkVersion returns the SdkSpec that corresponds to the target_sdk_version property of the current module,
// or from sdk_version if it is not set.
TargetSdkVersion(ctx EarlyModuleContext) SdkSpec
}
// SdkKind represents a particular category of an SDK spec like public, system, test, etc.
type SdkKind int
const (
SdkInvalid SdkKind = iota
SdkNone
SdkCore
SdkCorePlatform
SdkPublic
SdkSystem
SdkTest
SdkModule
SdkSystemServer
SdkPrivate
)
// String returns the string representation of this SdkKind
func (k SdkKind) String() string {
switch k {
case SdkPrivate:
return "private"
case SdkNone:
return "none"
case SdkPublic:
return "public"
case SdkSystem:
return "system"
case SdkTest:
return "test"
case SdkCore:
return "core"
case SdkCorePlatform:
return "core_platform"
case SdkModule:
return "module-lib"
case SdkSystemServer:
return "system-server"
default:
return "invalid"
}
}
// SdkSpec represents the kind and the version of an SDK for a module to build against
type SdkSpec struct {
Kind SdkKind
ApiLevel ApiLevel
Raw string
}
func (s SdkSpec) String() string {
return fmt.Sprintf("%s_%s", s.Kind, s.ApiLevel)
}
// Valid checks if this SdkSpec is well-formed. Note however that true doesn't mean that the
// specified SDK actually exists.
func (s SdkSpec) Valid() bool {
return s.Kind != SdkInvalid
}
// Specified checks if this SdkSpec is well-formed and is not "".
func (s SdkSpec) Specified() bool {
return s.Valid() && s.Kind != SdkPrivate
}
// whether the API surface is managed and versioned, i.e. has .txt file that
// get frozen on SDK freeze and changes get reviewed by API council.
func (s SdkSpec) Stable() bool {
if !s.Specified() {
return false
}
switch s.Kind {
case SdkNone:
// there is nothing to manage and version in this case; de facto stable API.
return true
case SdkCore, SdkPublic, SdkSystem, SdkModule, SdkSystemServer:
return true
case SdkCorePlatform, SdkTest, SdkPrivate:
return false
default:
panic(fmt.Errorf("unknown SdkKind=%v", s.Kind))
}
return false
}
// PrebuiltSdkAvailableForUnbundledBuild tells whether this SdkSpec can have a prebuilt SDK
// that can be used for unbundled builds.
func (s SdkSpec) PrebuiltSdkAvailableForUnbundledBuild() bool {
// "", "none", and "core_platform" are not available for unbundled build
// as we don't/can't have prebuilt stub for the versions
return s.Kind != SdkPrivate && s.Kind != SdkNone && s.Kind != SdkCorePlatform
}
func (s SdkSpec) ForVendorPartition(ctx EarlyModuleContext) SdkSpec {
// If BOARD_CURRENT_API_LEVEL_FOR_VENDOR_MODULES has a numeric value,
// use it instead of "current" for the vendor partition.
currentSdkVersion := ctx.DeviceConfig().CurrentApiLevelForVendorModules()
if currentSdkVersion == "current" {
return s
}
if s.Kind == SdkPublic || s.Kind == SdkSystem {
if s.ApiLevel.IsCurrent() {
if i, err := strconv.Atoi(currentSdkVersion); err == nil {
apiLevel := uncheckedFinalApiLevel(i)
return SdkSpec{s.Kind, apiLevel, s.Raw}
}
panic(fmt.Errorf("BOARD_CURRENT_API_LEVEL_FOR_VENDOR_MODULES must be either \"current\" or a number, but was %q", currentSdkVersion))
}
}
return s
}
// UsePrebuilt determines whether prebuilt SDK should be used for this SdkSpec with the given context.
func (s SdkSpec) UsePrebuilt(ctx EarlyModuleContext) bool {
switch s {
case SdkSpecNone, SdkSpecCorePlatform, SdkSpecPrivate:
return false
}
if s.ApiLevel.IsCurrent() {
// "current" can be built from source and be from prebuilt SDK
return ctx.Config().AlwaysUsePrebuiltSdks()
} else if !s.ApiLevel.IsPreview() {
// validation check
if s.Kind != SdkPublic && s.Kind != SdkSystem && s.Kind != SdkTest && s.Kind != SdkModule && s.Kind != SdkSystemServer {
panic(fmt.Errorf("prebuilt SDK is not not available for SdkKind=%q", s.Kind))
return false
}
// numbered SDKs are always from prebuilt
return true
}
return false
}
// EffectiveVersion converts an SdkSpec into the concrete ApiLevel that the module should use. For
// modules targeting an unreleased SDK (meaning it does not yet have a number) it returns
// FutureApiLevel(10000).
func (s SdkSpec) EffectiveVersion(ctx EarlyModuleContext) (ApiLevel, error) {
if !s.Valid() {
return s.ApiLevel, fmt.Errorf("invalid sdk version %q", s.Raw)
}
if ctx.DeviceSpecific() || ctx.SocSpecific() {
s = s.ForVendorPartition(ctx)
}
if !s.ApiLevel.IsPreview() {
return s.ApiLevel, nil
}
ret := ctx.Config().DefaultAppTargetSdk(ctx)
if ret.IsPreview() {
return FutureApiLevel, nil
}
return ret, nil
}
// EffectiveVersionString converts an SdkSpec into the concrete version string that the module
// should use. For modules targeting an unreleased SDK (meaning it does not yet have a number)
// it returns the codename (P, Q, R, etc.)
func (s SdkSpec) EffectiveVersionString(ctx EarlyModuleContext) (string, error) {
if !s.Valid() {
return s.ApiLevel.String(), fmt.Errorf("invalid sdk version %q", s.Raw)
}
if ctx.DeviceSpecific() || ctx.SocSpecific() {
s = s.ForVendorPartition(ctx)
}
if !s.ApiLevel.IsPreview() {
return s.ApiLevel.String(), nil
}
return ctx.Config().DefaultAppTargetSdk(ctx).String(), nil
}
var (
SdkSpecNone = SdkSpec{SdkNone, NoneApiLevel, "(no version)"}
SdkSpecPrivate = SdkSpec{SdkPrivate, FutureApiLevel, ""}
SdkSpecCorePlatform = SdkSpec{SdkCorePlatform, FutureApiLevel, "core_platform"}
)
func SdkSpecFrom(ctx EarlyModuleContext, str string) SdkSpec {
return SdkSpecFromWithConfig(ctx.Config(), str)
}
func SdkSpecFromWithConfig(config Config, str string) SdkSpec {
switch str {
// special cases first
case "":
return SdkSpecPrivate
case "none":
return SdkSpecNone
case "core_platform":
return SdkSpecCorePlatform
default:
// the syntax is [kind_]version
sep := strings.LastIndex(str, "_")
var kindString string
if sep == 0 {
return SdkSpec{SdkInvalid, NoneApiLevel, str}
} else if sep == -1 {
kindString = ""
} else {
kindString = str[0:sep]
}
versionString := str[sep+1 : len(str)]
var kind SdkKind
switch kindString {
case "":
kind = SdkPublic
case "core":
kind = SdkCore
case "system":
kind = SdkSystem
case "test":
kind = SdkTest
case "module":
kind = SdkModule
case "system_server":
kind = SdkSystemServer
default:
return SdkSpec{SdkInvalid, NoneApiLevel, str}
}
apiLevel, err := ApiLevelFromUserWithConfig(config, versionString)
if err != nil {
return SdkSpec{SdkInvalid, apiLevel, str}
}
return SdkSpec{kind, apiLevel, str}
}
}
func (s SdkSpec) ValidateSystemSdk(ctx EarlyModuleContext) bool {
// Ensures that the specified system SDK version is one of BOARD_SYSTEMSDK_VERSIONS (for vendor/product Java module)
// Assuming that BOARD_SYSTEMSDK_VERSIONS := 28 29,
// sdk_version of the modules in vendor/product that use system sdk must be either system_28, system_29 or system_current
if s.Kind != SdkSystem || s.ApiLevel.IsPreview() {
return true
}
allowedVersions := ctx.DeviceConfig().PlatformSystemSdkVersions()
if ctx.DeviceSpecific() || ctx.SocSpecific() || (ctx.ProductSpecific() && ctx.Config().EnforceProductPartitionInterface()) {
systemSdkVersions := ctx.DeviceConfig().SystemSdkVersions()
if len(systemSdkVersions) > 0 {
allowedVersions = systemSdkVersions
}
}
if len(allowedVersions) > 0 && !InList(s.ApiLevel.String(), allowedVersions) {
ctx.PropertyErrorf("sdk_version", "incompatible sdk version %q. System SDK version should be one of %q",
s.Raw, allowedVersions)
return false
}
return true
}