| // Copyright (C) 2018 The Android Open Source Project |
| // |
| // 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 aidl |
| |
| import ( |
| "android/soong/android" |
| "android/soong/bazel" |
| "android/soong/cc" |
| "android/soong/java" |
| "android/soong/phony" |
| "android/soong/rust" |
| |
| "fmt" |
| "path/filepath" |
| "regexp" |
| "sort" |
| "strconv" |
| "strings" |
| |
| "github.com/google/blueprint" |
| "github.com/google/blueprint/proptools" |
| ) |
| |
| const ( |
| aidlInterfaceSuffix = "_interface" |
| aidlMetadataSingletonName = "aidl_metadata_json" |
| aidlApiDir = "aidl_api" |
| aidlApiSuffix = "-api" |
| langCpp = "cpp" |
| langJava = "java" |
| langNdk = "ndk" |
| langRust = "rust" |
| langCppAnalyzer = "cpp-analyzer" |
| // TODO(b/161456198) remove the NDK platform backend as the 'platform' variant of the NDK |
| // backend serves the same purpose. |
| langNdkPlatform = "ndk_platform" |
| |
| currentVersion = "current" |
| ) |
| |
| var pctx = android.NewPackageContext("android/aidl") |
| |
| func init() { |
| pctx.Import("android/soong/android") |
| pctx.HostBinToolVariable("aidlCmd", "aidl") |
| pctx.HostBinToolVariable("aidlHashGen", "aidl_hash_gen") |
| pctx.SourcePathVariable("aidlToJniCmd", "system/tools/aidl/build/aidl_to_jni.py") |
| pctx.SourcePathVariable("aidlRustGlueCmd", "system/tools/aidl/build/aidl_rust_glue.py") |
| android.RegisterModuleType("aidl_interface", AidlInterfaceFactory) |
| android.PreArchMutators(registerPreArchMutators) |
| android.PreArchBp2BuildMutators(registerPreArchMutators) |
| android.PostDepsMutators(registerPostDepsMutators) |
| } |
| |
| func registerPreArchMutators(ctx android.RegisterMutatorsContext) { |
| ctx.BottomUp("addInterfaceDeps", addInterfaceDeps).Parallel() |
| ctx.BottomUp("checkImports", checkImports).Parallel() |
| ctx.TopDown("createAidlInterface", createAidlInterfaceMutator).Parallel() |
| } |
| |
| func registerPostDepsMutators(ctx android.RegisterMutatorsContext) { |
| ctx.BottomUp("checkAidlGeneratedModules", checkAidlGeneratedModules).Parallel() |
| } |
| |
| func createAidlInterfaceMutator(mctx android.TopDownMutatorContext) { |
| if g, ok := mctx.Module().(*aidlImplementationGenerator); ok { |
| g.GenerateImplementation(mctx) |
| } |
| } |
| |
| // A marker struct for AIDL-generated library modules |
| type AidlGeneratedModuleProperties struct{} |
| |
| func wrapLibraryFactory(factory func() android.Module) func() android.Module { |
| return func() android.Module { |
| m := factory() |
| // put a marker struct for AIDL-generated modules |
| m.AddProperties(&AidlGeneratedModuleProperties{}) |
| return m |
| } |
| } |
| |
| func isAidlGeneratedModule(module android.Module) bool { |
| for _, props := range module.GetProperties() { |
| // check if there's a marker struct |
| if _, ok := props.(*AidlGeneratedModuleProperties); ok { |
| return true |
| } |
| } |
| return false |
| } |
| |
| // AidlVersionInfo keeps the *-source module for each (aidl_interface & lang) and the list of |
| // not-frozen versions (which shouldn't be used by other modules) |
| type AidlVersionInfo struct { |
| notFrozen []string |
| requireFrozenReasons []string |
| sourceMap map[string]string |
| } |
| |
| var AidlVersionInfoProvider = blueprint.NewMutatorProvider(AidlVersionInfo{}, "checkAidlGeneratedModules") |
| |
| // Merges `other` version info into this one. |
| // Returns the pair of mismatching versions when there's conflict. Otherwise returns nil. |
| // For example, when a module depends on 'foo-V2-ndk', the map contains an entry of (foo, foo-V2-ndk-source). |
| // Merging (foo, foo-V1-ndk-source) and (foo, foo-V2-ndk-source) will fail and returns |
| // {foo-V1-ndk-source, foo-V2-ndk-source}. |
| func (info *AidlVersionInfo) merge(other AidlVersionInfo) []string { |
| info.notFrozen = append(info.notFrozen, other.notFrozen...) |
| info.requireFrozenReasons = append(info.requireFrozenReasons, other.requireFrozenReasons...) |
| |
| if other.sourceMap == nil { |
| return nil |
| } |
| if info.sourceMap == nil { |
| info.sourceMap = make(map[string]string) |
| } |
| for ifaceName, otherSourceName := range other.sourceMap { |
| if sourceName, ok := info.sourceMap[ifaceName]; ok { |
| if sourceName != otherSourceName { |
| return []string{sourceName, otherSourceName} |
| } |
| } else { |
| info.sourceMap[ifaceName] = otherSourceName |
| } |
| } |
| return nil |
| } |
| |
| func reportUsingNotFrozenError(ctx android.BaseModuleContext, notFrozen []string, requireFrozenReason []string) { |
| // TODO(b/154066686): Replace it with a common method instead of listing up module types. |
| // Test libraries are exempted. |
| if android.InList(ctx.ModuleType(), []string{"cc_test_library", "android_test", "cc_benchmark", "cc_test"}) { |
| return |
| } |
| for i, name := range notFrozen { |
| reason := requireFrozenReason[i] |
| ctx.ModuleErrorf("%v is an unfrozen development version, and it can't be used because %q", name, reason) |
| } |
| } |
| |
| func reportMultipleVersionError(ctx android.BaseModuleContext, violators []string) { |
| sort.Strings(violators) |
| ctx.ModuleErrorf("depends on multiple versions of the same aidl_interface: %s", strings.Join(violators, ", ")) |
| ctx.WalkDeps(func(child android.Module, parent android.Module) bool { |
| if android.InList(child.Name(), violators) { |
| ctx.ModuleErrorf("Dependency path: %s", ctx.GetPathString(true)) |
| return false |
| } |
| return true |
| }) |
| } |
| |
| func checkAidlGeneratedModules(mctx android.BottomUpMutatorContext) { |
| switch mctx.Module().(type) { |
| case *java.Library: |
| case *cc.Module: |
| case *rust.Module: |
| case *aidlGenRule: |
| default: |
| return |
| } |
| if gen, ok := mctx.Module().(*aidlGenRule); ok { |
| var notFrozen []string |
| var requireFrozenReasons []string |
| if gen.properties.NotFrozen { |
| notFrozen = []string{strings.TrimSuffix(mctx.ModuleName(), "-source")} |
| requireFrozenReasons = []string{gen.properties.RequireFrozenReason} |
| } |
| mctx.SetProvider(AidlVersionInfoProvider, AidlVersionInfo{ |
| notFrozen: notFrozen, |
| requireFrozenReasons: requireFrozenReasons, |
| sourceMap: map[string]string{ |
| gen.properties.BaseName + "-" + gen.properties.Lang: gen.Name(), |
| }, |
| }) |
| return |
| } |
| // Collect/merge AidlVersionInfos from direct dependencies |
| var info AidlVersionInfo |
| mctx.VisitDirectDeps(func(dep android.Module) { |
| if mctx.OtherModuleHasProvider(dep, AidlVersionInfoProvider) { |
| otherInfo := mctx.OtherModuleProvider(dep, AidlVersionInfoProvider).(AidlVersionInfo) |
| if violators := info.merge(otherInfo); violators != nil { |
| reportMultipleVersionError(mctx, violators) |
| } |
| } |
| }) |
| if !isAidlGeneratedModule(mctx.Module()) && len(info.notFrozen) > 0 { |
| reportUsingNotFrozenError(mctx, info.notFrozen, info.requireFrozenReasons) |
| } |
| if mctx.Failed() { |
| return |
| } |
| if info.sourceMap != nil || len(info.notFrozen) > 0 { |
| mctx.SetProvider(AidlVersionInfoProvider, info) |
| } |
| } |
| |
| func getPaths(ctx android.ModuleContext, rawSrcs []string, root string) (srcs android.Paths, imports []string) { |
| // TODO(b/189288369): move this to android.PathsForModuleSrcSubDir(ctx, srcs, subdir) |
| for _, src := range rawSrcs { |
| if m, _ := android.SrcIsModuleWithTag(src); m != "" { |
| srcs = append(srcs, android.PathsForModuleSrc(ctx, []string{src})...) |
| } else { |
| srcs = append(srcs, android.PathsWithModuleSrcSubDir(ctx, android.PathsForModuleSrc(ctx, []string{src}), root)...) |
| } |
| } |
| |
| if len(srcs) == 0 { |
| ctx.PropertyErrorf("srcs", "No sources provided in %v", root) |
| } |
| |
| // gather base directories from input .aidl files |
| for _, src := range srcs { |
| if src.Ext() != ".aidl" { |
| // Silently ignore non-aidl files as some filegroups have both java and aidl files together |
| continue |
| } |
| baseDir := strings.TrimSuffix(src.String(), src.Rel()) |
| baseDir = strings.TrimSuffix(baseDir, "/") |
| if baseDir != "" && !android.InList(baseDir, imports) { |
| imports = append(imports, baseDir) |
| } |
| } |
| |
| return srcs, imports |
| } |
| |
| func isRelativePath(path string) bool { |
| if path == "" { |
| return true |
| } |
| return filepath.Clean(path) == path && path != ".." && |
| !strings.HasPrefix(path, "../") && !strings.HasPrefix(path, "/") |
| } |
| |
| type CommonBackendProperties struct { |
| // Whether to generate code in the corresponding backend. |
| // Default: |
| // - for Java/NDK/CPP backends - True |
| // - for Rust backend - False |
| Enabled *bool |
| Apex_available []string |
| |
| // The minimum version of the sdk that the compiled artifacts will run against |
| // For native modules, the property needs to be set when a module is a part of mainline modules(APEX). |
| // Forwarded to generated java/native module. |
| Min_sdk_version *string |
| |
| // Whether tracing should be added to the interface. |
| Gen_trace *bool |
| } |
| |
| type CommonNativeBackendProperties struct { |
| CommonBackendProperties |
| |
| // Must be NDK libraries, for stable types. |
| Additional_shared_libraries []string |
| |
| // cflags to forward to native compilation. This is expected to be |
| // used more for AIDL compiler developers than being actually |
| // practical. |
| Cflags []string |
| |
| // Whether to generate additional code for gathering information |
| // about the transactions. |
| // Default: false |
| Gen_log *bool |
| |
| // VNDK properties for correspdoning backend. |
| cc.VndkProperties |
| } |
| |
| type DumpApiProperties struct { |
| // Dumps without license header (assuming it is the first comment in .aidl file). Default: false |
| No_license *bool |
| } |
| |
| type aidlInterfaceProperties struct { |
| // Vndk properties for C++/NDK libraries only (preferred to use backend-specific settings) |
| cc.VndkProperties |
| |
| // How to interpret VNDK options. We only want one library in the VNDK (not multiple |
| // versions, since this would be a waste of space/unclear, and ultimately we want all |
| // code in a given release to be updated to use a specific version). By default, this |
| // puts either the latest stable version of the library or, if there is no stable |
| // version, the unstable version of the library in the VNDK. When using this field, |
| // explicitly set it to one of the values in the 'versions' field to put that version |
| // in the VNDK or set it to the next version (1 higher than this) to mean the version |
| // that will be frozen in the next update. |
| Vndk_use_version *string |
| |
| // Whether the library can be installed on the vendor image. |
| Vendor_available *bool |
| |
| // Whether the library can be installed on the odm image. |
| Odm_available *bool |
| |
| // Whether the library can be installed on the product image. |
| Product_available *bool |
| |
| // Whether the library can be installed on the recovery image. |
| Recovery_available *bool |
| |
| // Whether the library can be loaded multiple times into the same process |
| Double_loadable *bool |
| |
| // Whether the library can be used on host |
| Host_supported *bool |
| |
| // Whether tracing should be added to the interface. |
| Gen_trace *bool |
| |
| // Top level directories for includes. |
| // TODO(b/128940869): remove it if aidl_interface can depend on framework.aidl |
| Include_dirs []string |
| // Relative path for includes. By default assumes AIDL path is relative to current directory. |
| Local_include_dir string |
| |
| // List of .aidl files which compose this interface. |
| Srcs []string `android:"path"` |
| |
| // List of aidl_interface modules that this uses. If one of your AIDL interfaces uses an |
| // interface or parcelable from another aidl_interface, you should put its name here. |
| // It could be an aidl_interface solely or with version(such as -V1) |
| Imports []string |
| |
| // Stability promise. Currently only supports "vintf". |
| // If this is unset, this corresponds to an interface with stability within |
| // this compilation context (so an interface loaded here can only be used |
| // with things compiled together, e.g. on the system.img). |
| // If this is set to "vintf", this corresponds to a stability promise: the |
| // interface must be kept stable as long as it is used. |
| Stability *string |
| |
| // If true, this interface is frozen and does not have any changes since the last |
| // frozen version. |
| // If false, there are changes to this interface between the last frozen version (N) and |
| // the current version (N + 1). |
| Frozen *bool |
| |
| // Deprecated: Use `versions_with_info` instead. Don't use `versions` property directly. |
| Versions []string |
| |
| // Previous API versions that are now frozen. The version that is last in |
| // the list is considered as the most recent version. |
| // The struct contains both version and imports information per a version. |
| // Until versions property is removed, don't use `versions_with_info` directly. |
| Versions_with_info []struct { |
| Version string |
| Imports []string |
| } |
| |
| // Use aidlInterface.getVersions() |
| VersionsInternal []string `blueprint:"mutated"` |
| |
| // The minimum version of the sdk that the compiled artifacts will run against |
| // For native modules, the property needs to be set when a module is a part of mainline modules(APEX). |
| // Forwarded to generated java/native module. This can be overridden by |
| // backend.<name>.min_sdk_version. |
| Min_sdk_version *string |
| |
| Backend struct { |
| // Backend of the compiler generating code for Java clients. |
| // When enabled, this creates a target called "<name>-java". |
| Java struct { |
| CommonBackendProperties |
| // Set to the version of the sdk to compile against |
| // Default: system_current |
| Sdk_version *string |
| // Whether to compile against platform APIs instead of |
| // an SDK. |
| Platform_apis *bool |
| // Whether RPC features are enabled (requires API level 32) |
| // TODO(b/175819535): enable this automatically? |
| Gen_rpc *bool |
| // Lint properties for generated java module |
| java.LintProperties |
| } |
| // Backend of the compiler generating code for C++ clients using |
| // libbinder (unstable C++ interface) |
| // When enabled, this creates a target called "<name>-cpp". |
| Cpp struct { |
| CommonNativeBackendProperties |
| } |
| // Backend of the compiler generating code for C++ clients using libbinder_ndk |
| // (stable C interface to system's libbinder) When enabled, this creates a target |
| // called "<name>-V<ver>-ndk" (for both apps and platform) and |
| // "<name>-V<ver>-ndk_platform" (for platform only). |
| // TODO(b/161456198): remove the ndk_platform backend as the ndk backend can serve |
| // the same purpose. |
| Ndk struct { |
| CommonNativeBackendProperties |
| |
| // Set to the version of the sdk to compile against, for the NDK |
| // variant. |
| // Default: current |
| Sdk_version *string |
| |
| // If set to false, the ndk backend is exclusive to platform and is not |
| // available to applications. Default is true (i.e. available to both |
| // applications and platform). |
| Apps_enabled *bool |
| } |
| // Backend of the compiler generating code for Rust clients. |
| // When enabled, this creates a target called "<name>-rust". |
| Rust struct { |
| CommonBackendProperties |
| } |
| } |
| |
| // Marks that this interface does not need to be stable. When set to true, the build system |
| // doesn't create the API dump and require it to be updated. Default is false. |
| Unstable *bool |
| |
| // Optional flags to be passed to the AIDL compiler. e.g. "-Weverything" |
| Flags []string |
| |
| // --dumpapi options |
| Dumpapi DumpApiProperties |
| |
| // List of aidl_interface_headers modules that provide include dependencies |
| // for the AIDL tool. |
| Headers []string |
| } |
| |
| type aidlInterface struct { |
| android.ModuleBase |
| android.BazelModuleBase |
| android.DefaultableModuleBase |
| |
| properties aidlInterfaceProperties |
| |
| computedTypes []string |
| |
| // list of module names that are created for this interface |
| internalModuleNames []string |
| |
| // map for version to preprocessed.aidl file. |
| // There's two additional alias for versions: |
| // - ""(empty) is for ToT |
| // - "latest" is for i.latestVersion() |
| preprocessed map[string]android.WritablePath |
| } |
| |
| func (i *aidlInterface) shouldGenerateJavaBackend() bool { |
| // explicitly true if not specified to give early warning to devs |
| return proptools.BoolDefault(i.properties.Backend.Java.Enabled, true) |
| } |
| |
| func (i *aidlInterface) shouldGenerateCppBackend() bool { |
| // explicitly true if not specified to give early warning to devs |
| return proptools.BoolDefault(i.properties.Backend.Cpp.Enabled, true) |
| } |
| |
| func (i *aidlInterface) shouldGenerateNdkBackend() bool { |
| // explicitly true if not specified to give early warning to devs |
| return proptools.BoolDefault(i.properties.Backend.Ndk.Enabled, true) |
| } |
| |
| // Returns whether the ndk backend supports applications or not. Default is `true`. `false` is |
| // returned when `apps_enabled` is explicitly set to false or the interface is exclusive to vendor |
| // (i.e. `vendor: true`). Note that the ndk_platform backend (which will be removed in the future) |
| // is not affected by this. In other words, it is always exclusive for the platform, as its name |
| // clearly shows. |
| func (i *aidlInterface) shouldGenerateAppNdkBackend() bool { |
| return i.shouldGenerateNdkBackend() && |
| proptools.BoolDefault(i.properties.Backend.Ndk.Apps_enabled, true) && |
| !i.SocSpecific() |
| } |
| |
| func (i *aidlInterface) shouldGenerateRustBackend() bool { |
| return i.properties.Backend.Rust.Enabled != nil && *i.properties.Backend.Rust.Enabled |
| } |
| |
| func (i *aidlInterface) minSdkVersion(lang string) *string { |
| var ver *string |
| switch lang { |
| case langCpp: |
| ver = i.properties.Backend.Cpp.Min_sdk_version |
| case langJava: |
| ver = i.properties.Backend.Java.Min_sdk_version |
| case langNdk, langNdkPlatform: |
| ver = i.properties.Backend.Ndk.Min_sdk_version |
| case langRust: |
| ver = i.properties.Backend.Rust.Min_sdk_version |
| default: |
| panic(fmt.Errorf("unsupported language backend %q\n", lang)) |
| } |
| if ver == nil { |
| return i.properties.Min_sdk_version |
| } |
| return ver |
| } |
| |
| func (i *aidlInterface) genTrace(lang string) bool { |
| var ver *bool |
| switch lang { |
| case langCpp: |
| ver = i.properties.Backend.Cpp.Gen_trace |
| if ver == nil { |
| // Enable tracing for all cpp backends by default |
| ver = proptools.BoolPtr(true) |
| } |
| case langJava: |
| ver = i.properties.Backend.Java.Gen_trace |
| if ver == nil && proptools.Bool(i.properties.Backend.Java.Platform_apis) { |
| // Enable tracing for all Java backends using platform APIs |
| // TODO(161393989) Once we generate ATRACE_TAG_APP instead of ATRACE_TAG_AIDL, |
| // this can be removed and we can start generating traces in all apps. |
| ver = proptools.BoolPtr(true) |
| } |
| case langNdk, langNdkPlatform: |
| ver = i.properties.Backend.Ndk.Gen_trace |
| case langRust: // unsupported b/236880829 |
| ver = i.properties.Backend.Rust.Gen_trace |
| case langCppAnalyzer: |
| *ver = false |
| default: |
| panic(fmt.Errorf("unsupported language backend %q\n", lang)) |
| } |
| if ver == nil { |
| ver = i.properties.Gen_trace |
| } |
| return proptools.Bool(ver) |
| } |
| |
| // Dep to *-api module(aidlApi) |
| type apiDepTag struct { |
| blueprint.BaseDependencyTag |
| name string |
| } |
| |
| type importInterfaceDepTag struct { |
| blueprint.BaseDependencyTag |
| anImport string |
| } |
| |
| type interfaceDepTag struct { |
| blueprint.BaseDependencyTag |
| } |
| |
| type interfaceHeadersDepTag struct { |
| blueprint.BaseDependencyTag |
| } |
| |
| var ( |
| // Dep from *-source (aidlGenRule) to *-api (aidlApi) |
| apiDep = apiDepTag{name: "api"} |
| // Dep from *-api (aidlApi) to *-api (aidlApi), representing imported interfaces |
| importApiDep = apiDepTag{name: "imported-api"} |
| // Dep to original *-interface (aidlInterface) |
| interfaceDep = interfaceDepTag{} |
| // Dep for a header interface |
| interfaceHeadersDep = interfaceHeadersDepTag{} |
| ) |
| |
| func addImportedInterfaceDeps(ctx android.BottomUpMutatorContext, imports []string) { |
| for _, anImport := range imports { |
| name, _ := parseModuleWithVersion(anImport) |
| ctx.AddDependency(ctx.Module(), importInterfaceDepTag{anImport: anImport}, name+aidlInterfaceSuffix) |
| } |
| } |
| |
| // Run custom "Deps" mutator between AIDL modules created at LoadHook stage. |
| // We can't use the "DepsMutator" for these dependencies because |
| // - We need to create library modules (cc/java/...) before "arch" mutator. Note that cc_library |
| // |
| // should be mutated by os/image/arch mutators as well. |
| // |
| // - When creating library modules, we need to access the original interface and its imported |
| // |
| // interfaces to determine which version to use. See aidlInterface.getImportWithVersion. |
| func addInterfaceDeps(mctx android.BottomUpMutatorContext) { |
| switch i := mctx.Module().(type) { |
| case *aidlInterface: |
| // In fact this isn't necessary because soong checks dependencies on undefined modules. |
| // But since aidl_interface overrides its name internally, this provides better error message. |
| for _, anImportWithVersion := range i.properties.Imports { |
| anImport, _ := parseModuleWithVersion(anImportWithVersion) |
| if !mctx.OtherModuleExists(anImport + aidlInterfaceSuffix) { |
| if !mctx.Config().AllowMissingDependencies() { |
| mctx.PropertyErrorf("imports", "Import does not exist: "+anImport) |
| } |
| } |
| } |
| if mctx.Failed() { |
| return |
| } |
| addImportedInterfaceDeps(mctx, i.properties.Imports) |
| |
| for _, header := range i.properties.Headers { |
| mctx.AddDependency(i, interfaceHeadersDep, header) |
| } |
| case *aidlImplementationGenerator: |
| mctx.AddDependency(i, interfaceDep, i.properties.AidlInterfaceName+aidlInterfaceSuffix) |
| addImportedInterfaceDeps(mctx, i.properties.Imports) |
| case *rust.Module: |
| for _, props := range i.GetProperties() { |
| if sp, ok := props.(*aidlRustSourceProviderProperties); ok { |
| mctx.AddDependency(i, interfaceDep, sp.AidlInterfaceName+aidlInterfaceSuffix) |
| addImportedInterfaceDeps(mctx, sp.Imports) |
| break |
| } |
| } |
| case *aidlApi: |
| mctx.AddDependency(i, interfaceDep, i.properties.BaseName+aidlInterfaceSuffix) |
| addImportedInterfaceDeps(mctx, i.properties.Imports) |
| for _, anImport := range i.properties.Imports { |
| name, _ := parseModuleWithVersion(anImport) |
| mctx.AddDependency(i, importApiDep, name+aidlApiSuffix) |
| } |
| for _, header := range i.properties.Headers { |
| mctx.AddDependency(i, interfaceHeadersDep, header) |
| } |
| case *aidlGenRule: |
| mctx.AddDependency(i, interfaceDep, i.properties.BaseName+aidlInterfaceSuffix) |
| addImportedInterfaceDeps(mctx, i.properties.Imports) |
| if !proptools.Bool(i.properties.Unstable) { |
| // for checkapi timestamps |
| mctx.AddDependency(i, apiDep, i.properties.BaseName+aidlApiSuffix) |
| } |
| for _, header := range i.properties.Headers { |
| mctx.AddDependency(i, interfaceHeadersDep, header) |
| } |
| } |
| } |
| |
| // checkImports checks if "import:" property is valid. |
| // In fact, this isn't necessary because Soong can check/report when we add a dependency to |
| // undefined/unknown module. But module names are very implementation specific and may not be easy |
| // to understand. For example, when foo (with java enabled) depends on bar (with java disabled), the |
| // error message would look like "foo-V2-java depends on unknown module `bar-V3-java`", which isn't |
| // clear that backend.java.enabled should be turned on. |
| func checkImports(mctx android.BottomUpMutatorContext) { |
| if i, ok := mctx.Module().(*aidlInterface); ok { |
| mctx.VisitDirectDeps(func(dep android.Module) { |
| tag, ok := mctx.OtherModuleDependencyTag(dep).(importInterfaceDepTag) |
| if !ok { |
| return |
| } |
| other := dep.(*aidlInterface) |
| anImport := other.ModuleBase.Name() |
| anImportWithVersion := tag.anImport |
| _, version := parseModuleWithVersion(tag.anImport) |
| |
| candidateVersions := other.getVersions() |
| if !proptools.Bool(other.properties.Frozen) { |
| candidateVersions = concat(candidateVersions, []string{other.nextVersion()}) |
| } |
| |
| if version == "" { |
| if !proptools.Bool(other.properties.Unstable) { |
| mctx.PropertyErrorf("imports", "%q depends on %q but does not specify a version (must be one of %q)", i.ModuleBase.Name(), anImport, candidateVersions) |
| } |
| } else { |
| if !android.InList(version, candidateVersions) { |
| mctx.PropertyErrorf("imports", "%q depends on %q version %q(%q), which doesn't exist. The version must be one of %q", i.ModuleBase.Name(), anImport, version, anImportWithVersion, candidateVersions) |
| } |
| } |
| if i.shouldGenerateJavaBackend() && !other.shouldGenerateJavaBackend() { |
| mctx.PropertyErrorf("backend.java.enabled", |
| "Java backend not enabled in the imported AIDL interface %q", anImport) |
| } |
| |
| if i.shouldGenerateCppBackend() && !other.shouldGenerateCppBackend() { |
| mctx.PropertyErrorf("backend.cpp.enabled", |
| "C++ backend not enabled in the imported AIDL interface %q", anImport) |
| } |
| |
| if i.shouldGenerateNdkBackend() && !other.shouldGenerateNdkBackend() { |
| mctx.PropertyErrorf("backend.ndk.enabled", |
| "NDK backend not enabled in the imported AIDL interface %q", anImport) |
| } |
| |
| if i.shouldGenerateRustBackend() && !other.shouldGenerateRustBackend() { |
| mctx.PropertyErrorf("backend.rust.enabled", |
| "Rust backend not enabled in the imported AIDL interface %q", anImport) |
| } |
| |
| if i.isFrozen() && other.isExplicitlyUnFrozen() && version == "" { |
| mctx.PropertyErrorf("frozen", |
| "%q imports %q which is not frozen. Either %q must set 'frozen: false' or must explicitly import %q where * is one of %q", |
| i.ModuleBase.Name(), anImport, i.ModuleBase.Name(), anImport+"-V*", candidateVersions) |
| } |
| if i.Owner() == "" && other.Owner() != "" { |
| mctx.PropertyErrorf("imports", |
| "%q imports %q which is an interface owned by %q. This is not allowed because the owned interface will not be frozen at the same time.", |
| i.ModuleBase.Name(), anImport, other.Owner()) |
| } |
| }) |
| } |
| } |
| |
| func (i *aidlInterface) checkGenTrace(mctx android.DefaultableHookContext) { |
| if !proptools.Bool(i.properties.Gen_trace) { |
| return |
| } |
| if i.shouldGenerateJavaBackend() && !proptools.Bool(i.properties.Backend.Java.Platform_apis) { |
| mctx.PropertyErrorf("gen_trace", "must be false when Java backend is enabled and platform_apis is false") |
| } |
| } |
| |
| func (i *aidlInterface) checkStability(mctx android.DefaultableHookContext) { |
| if i.properties.Stability == nil { |
| return |
| } |
| |
| if proptools.Bool(i.properties.Unstable) { |
| mctx.PropertyErrorf("stability", "must be empty when \"unstable\" is true") |
| } |
| |
| // TODO(b/136027762): should we allow more types of stability (e.g. for APEX) or |
| // should we switch this flag to be something like "vintf { enabled: true }" |
| isVintf := "vintf" == proptools.String(i.properties.Stability) |
| if !isVintf { |
| mctx.PropertyErrorf("stability", "must be empty or \"vintf\"") |
| } |
| } |
| func (i *aidlInterface) checkVersions(mctx android.DefaultableHookContext) { |
| if len(i.properties.Versions) > 0 && len(i.properties.Versions_with_info) > 0 { |
| mctx.ModuleErrorf("versions:%q and versions_with_info:%q cannot be used at the same time. Use versions_with_info instead of versions.", i.properties.Versions, i.properties.Versions_with_info) |
| } |
| |
| if len(i.properties.Versions) > 0 { |
| i.properties.VersionsInternal = make([]string, len(i.properties.Versions)) |
| copy(i.properties.VersionsInternal, i.properties.Versions) |
| } else if len(i.properties.Versions_with_info) > 0 { |
| i.properties.VersionsInternal = make([]string, len(i.properties.Versions_with_info)) |
| for idx, value := range i.properties.Versions_with_info { |
| i.properties.VersionsInternal[idx] = value.Version |
| for _, im := range value.Imports { |
| if !hasVersionSuffix(im) { |
| mctx.ModuleErrorf("imports in versions_with_info must specify its version, but %s. Add a version suffix(such as %s-V1).", im, im) |
| return |
| } |
| } |
| } |
| } |
| |
| versions := make(map[string]bool) |
| intVersions := make([]int, 0, len(i.getVersions())) |
| for _, ver := range i.getVersions() { |
| if _, dup := versions[ver]; dup { |
| mctx.PropertyErrorf("versions", "duplicate found", ver) |
| continue |
| } |
| versions[ver] = true |
| n, err := strconv.Atoi(ver) |
| if err != nil { |
| mctx.PropertyErrorf("versions", "%q is not an integer", ver) |
| continue |
| } |
| if n <= 0 { |
| mctx.PropertyErrorf("versions", "should be > 0, but is %v", ver) |
| continue |
| } |
| intVersions = append(intVersions, n) |
| |
| } |
| if !mctx.Failed() && !sort.IntsAreSorted(intVersions) { |
| mctx.PropertyErrorf("versions", "should be sorted, but is %v", i.getVersions()) |
| } |
| } |
| func (i *aidlInterface) checkVndkUseVersion(mctx android.DefaultableHookContext) { |
| if i.properties.Vndk_use_version == nil { |
| return |
| } |
| if *i.properties.Vndk_use_version == i.nextVersion() { |
| return |
| } |
| for _, ver := range i.getVersions() { |
| if *i.properties.Vndk_use_version == ver { |
| return |
| } |
| } |
| mctx.PropertyErrorf("vndk_use_version", "Specified version %q does not exist", *i.properties.Vndk_use_version) |
| } |
| |
| func (i *aidlInterface) nextVersion() string { |
| if proptools.Bool(i.properties.Unstable) { |
| return "" |
| } |
| return nextVersion(i.getVersions()) |
| } |
| |
| func nextVersion(versions []string) string { |
| if len(versions) == 0 { |
| return "1" |
| } |
| ver := versions[len(versions)-1] |
| i, err := strconv.Atoi(ver) |
| if err != nil { |
| panic(err) |
| } |
| return strconv.Itoa(i + 1) |
| } |
| |
| func (i *aidlInterface) latestVersion() string { |
| if !i.hasVersion() { |
| return "0" |
| } |
| return i.getVersions()[len(i.getVersions())-1] |
| } |
| |
| func (i *aidlInterface) hasVersion() bool { |
| return len(i.getVersions()) > 0 |
| } |
| |
| func (i *aidlInterface) getVersions() []string { |
| return i.properties.VersionsInternal |
| } |
| |
| func (i *aidlInterface) isFrozen() bool { |
| return proptools.Bool(i.properties.Frozen) |
| } |
| |
| // in order to keep original behavior for certain operations, we may want to |
| // check if frozen is set. |
| func (i *aidlInterface) isExplicitlyUnFrozen() bool { |
| return i.properties.Frozen != nil && !proptools.Bool(i.properties.Frozen) |
| } |
| |
| func hasVersionSuffix(moduleName string) bool { |
| hasVersionSuffix, _ := regexp.MatchString("-V\\d+$", moduleName) |
| return hasVersionSuffix |
| } |
| |
| func parseModuleWithVersion(moduleName string) (string, string) { |
| if hasVersionSuffix(moduleName) { |
| versionIdx := strings.LastIndex(moduleName, "-V") |
| if versionIdx == -1 { |
| panic("-V must exist in this context") |
| } |
| return moduleName[:versionIdx], moduleName[versionIdx+len("-V"):] |
| } |
| return moduleName, "" |
| } |
| |
| func trimVersionSuffixInList(moduleNames []string) []string { |
| return wrapFunc("", moduleNames, "", func(moduleName string) string { |
| moduleNameWithoutVersion, _ := parseModuleWithVersion(moduleName) |
| return moduleNameWithoutVersion |
| }) |
| } |
| |
| func (i *aidlInterface) checkRequireFrozenAndReason(mctx android.EarlyModuleContext) (bool, string) { |
| if proptools.Bool(i.properties.Unstable) { |
| return false, "it's an unstable interface" |
| } |
| |
| if proptools.Bool(i.properties.Frozen) { |
| return true, "it's explicitly marked as `frozen: true`" |
| } |
| |
| if i.Owner() == "" { |
| if !mctx.Config().DefaultAppTargetSdk(mctx).IsPreview() { |
| return true, "this is a release branch - freeze it or set 'owners:'" |
| } else if mctx.Config().IsEnvTrue("AIDL_FROZEN_REL") { |
| return true, "this is a release branch (simulated by setting AIDL_FROZEN_REL) - freeze it or set 'owners:'" |
| } |
| } else { |
| // has an OWNER |
| // REL branches don't enforce downstream interfaces or owned interfaces |
| // to be frozen. Instead, these interfaces are verified by other tests |
| // like vts_treble_vintf_vendor_test |
| if android.InList(i.Owner(), strings.Fields(mctx.Config().Getenv("AIDL_FROZEN_OWNERS"))) { |
| return true, "the owner field is in environment variable AIDL_FROZEN_OWNERS" |
| } |
| } |
| |
| return false, "by default, we don't require the interface to be frozen" |
| } |
| |
| func aidlInterfaceHook(mctx android.DefaultableHookContext, i *aidlInterface) { |
| if hasVersionSuffix(i.ModuleBase.Name()) { |
| mctx.PropertyErrorf("name", "aidl_interface should not have '-V<number> suffix") |
| } |
| if !isRelativePath(i.properties.Local_include_dir) { |
| mctx.PropertyErrorf("local_include_dir", "must be relative path: "+i.properties.Local_include_dir) |
| } |
| |
| i.checkStability(mctx) |
| i.checkVersions(mctx) |
| i.checkVndkUseVersion(mctx) |
| i.checkGenTrace(mctx) |
| |
| if mctx.Failed() { |
| return |
| } |
| |
| var libs []string |
| |
| unstable := proptools.Bool(i.properties.Unstable) |
| |
| if unstable { |
| if i.hasVersion() { |
| mctx.PropertyErrorf("versions", "cannot have versions for an unstable interface") |
| return |
| } |
| if i.properties.Stability != nil { |
| mctx.ModuleErrorf("unstable:true and stability:%q cannot happen at the same time", i.properties.Stability) |
| return |
| } |
| } |
| |
| if i.isFrozen() { |
| if !i.hasVersion() { |
| mctx.PropertyErrorf("frozen", "cannot be frozen without versions") |
| return |
| } |
| } |
| |
| if !unstable && mctx.Namespace().Path != "." && i.Owner() == "" { |
| mctx.PropertyErrorf("owner", "aidl_interface in a soong_namespace must have the 'owner' property set.") |
| } |
| |
| requireFrozenVersion, requireFrozenReason := i.checkRequireFrozenAndReason(mctx) |
| |
| // surface error early, main check is via checkUnstableModuleMutator |
| if requireFrozenVersion && !i.hasVersion() { |
| mctx.PropertyErrorf("versions", "must be set (need to be frozen) because: %q", requireFrozenReason) |
| } |
| |
| vndkEnabled := proptools.Bool(i.properties.VndkProperties.Vndk.Enabled) || |
| proptools.Bool(i.properties.Backend.Cpp.CommonNativeBackendProperties.VndkProperties.Vndk.Enabled) || |
| proptools.Bool(i.properties.Backend.Ndk.CommonNativeBackendProperties.VndkProperties.Vndk.Enabled) |
| |
| if vndkEnabled && !proptools.Bool(i.properties.Unstable) { |
| if i.properties.Frozen == nil { |
| mctx.PropertyErrorf("frozen", "true or false must be specified when the VNDK is enabled on a versioned interface (not `unstable: true`)") |
| } |
| if !proptools.Bool(i.properties.Frozen) && i.properties.Vndk_use_version == nil { |
| mctx.PropertyErrorf("vndk_use_version", "must be specified if interface is unfrozen (or specify 'frozen: false')") |
| } |
| } |
| |
| versions := i.getVersions() |
| nextVersion := i.nextVersion() |
| shouldGenerateLangBackendMap := map[string]bool{ |
| langCpp: i.shouldGenerateCppBackend(), |
| langNdk: i.shouldGenerateNdkBackend(), |
| langJava: i.shouldGenerateJavaBackend(), |
| langRust: i.shouldGenerateRustBackend()} |
| |
| // The ndk_platform backend is generated only when explicitly requested. This will |
| // eventually be completely removed the devices in the long tail are gone. |
| if mctx.DeviceConfig().GenerateAidlNdkPlatformBackend() { |
| shouldGenerateLangBackendMap[langNdkPlatform] = i.shouldGenerateNdkBackend() |
| } |
| |
| for lang, shouldGenerate := range shouldGenerateLangBackendMap { |
| if !shouldGenerate { |
| continue |
| } |
| libs = append(libs, addLibrary(mctx, i, nextVersion, lang, requireFrozenVersion, requireFrozenReason)) |
| for _, version := range versions { |
| libs = append(libs, addLibrary(mctx, i, version, lang, false, "this is a known frozen version")) |
| } |
| } |
| |
| // In the future, we may want to force the -cpp backend to be on host, |
| // and limit its visibility, even if it's not created normally |
| if i.shouldGenerateCppBackend() && len(i.properties.Imports) == 0 { |
| libs = append(libs, addLibrary(mctx, i, nextVersion, langCppAnalyzer, false, "analysis always uses latest version even if frozen")) |
| } |
| |
| if unstable { |
| apiDirRoot := filepath.Join(aidlApiDir, i.ModuleBase.Name()) |
| aidlDumps, _ := mctx.GlobWithDeps(filepath.Join(mctx.ModuleDir(), apiDirRoot, "**/*.aidl"), nil) |
| if len(aidlDumps) != 0 { |
| mctx.PropertyErrorf("unstable", "The interface is configured as unstable, "+ |
| "but API dumps exist under %q. Unstable interface cannot have dumps.", apiDirRoot) |
| } |
| } else { |
| addApiModule(mctx, i) |
| } |
| |
| // Reserve this module name for future use |
| mctx.CreateModule(phony.PhonyFactory, &phonyProperties{ |
| Name: proptools.StringPtr(i.ModuleBase.Name()), |
| }) |
| |
| i.internalModuleNames = libs |
| } |
| |
| func (i *aidlInterface) commonBackendProperties(lang string) CommonBackendProperties { |
| switch lang { |
| case langCpp: |
| return i.properties.Backend.Cpp.CommonBackendProperties |
| case langJava: |
| return i.properties.Backend.Java.CommonBackendProperties |
| case langNdk, langNdkPlatform: |
| return i.properties.Backend.Ndk.CommonBackendProperties |
| case langRust: |
| return i.properties.Backend.Rust.CommonBackendProperties |
| default: |
| panic(fmt.Errorf("unsupported language backend %q\n", lang)) |
| } |
| } |
| |
| func (i *aidlInterface) Name() string { |
| return i.ModuleBase.Name() + aidlInterfaceSuffix |
| } |
| |
| func (i *aidlInterface) GenerateAndroidBuildActions(ctx android.ModuleContext) { |
| srcs, _ := getPaths(ctx, i.properties.Srcs, i.properties.Local_include_dir) |
| for _, src := range srcs { |
| computedType := strings.TrimSuffix(strings.ReplaceAll(src.Rel(), "/", "."), ".aidl") |
| i.computedTypes = append(i.computedTypes, computedType) |
| } |
| |
| i.preprocessed = make(map[string]android.WritablePath) |
| // generate (len(versions) + 1) preprocessed.aidl files |
| for _, version := range concat(i.getVersions(), []string{i.nextVersion()}) { |
| i.preprocessed[version] = i.buildPreprocessed(ctx, version) |
| } |
| // helpful aliases |
| if !proptools.Bool(i.properties.Unstable) { |
| if i.hasVersion() { |
| i.preprocessed["latest"] = i.preprocessed[i.latestVersion()] |
| } else { |
| // when we have no frozen versions yet, use "next version" as latest |
| i.preprocessed["latest"] = i.preprocessed[i.nextVersion()] |
| } |
| i.preprocessed[""] = i.preprocessed[i.nextVersion()] |
| } |
| } |
| |
| func (i *aidlInterface) getImportsForVersion(version string) []string { |
| // `Imports` is used when version == i.nextVersion() or`versions` is defined instead of `versions_with_info` |
| importsSrc := i.properties.Imports |
| for _, v := range i.properties.Versions_with_info { |
| if v.Version == version { |
| importsSrc = v.Imports |
| break |
| } |
| } |
| imports := make([]string, len(importsSrc)) |
| copy(imports, importsSrc) |
| |
| return imports |
| } |
| |
| func (i *aidlInterface) getImports(version string) map[string]string { |
| imports := make(map[string]string) |
| imports_src := i.getImportsForVersion(version) |
| |
| useLatestStable := !proptools.Bool(i.properties.Unstable) && version != "" && version != i.nextVersion() |
| for _, importString := range imports_src { |
| name, targetVersion := parseModuleWithVersion(importString) |
| if targetVersion == "" && useLatestStable { |
| targetVersion = "latest" |
| } |
| imports[name] = targetVersion |
| } |
| return imports |
| } |
| |
| // generate preprocessed.aidl which contains only types with evaluated constants. |
| // "imports" will use preprocessed.aidl with -p flag to avoid parsing the entire transitive list |
| // of dependencies. |
| func (i *aidlInterface) buildPreprocessed(ctx android.ModuleContext, version string) android.WritablePath { |
| deps := getDeps(ctx, i.getImports(version)) |
| |
| preprocessed := android.PathForModuleOut(ctx, version, "preprocessed.aidl") |
| rb := android.NewRuleBuilder(pctx, ctx) |
| srcs, root_dir := i.srcsForVersion(ctx, version) |
| |
| if len(srcs) == 0 { |
| ctx.PropertyErrorf("srcs", "No sources for a previous version in %v. Was a version manually added to .bp file? This is added automatically by <module>-freeze-api.", root_dir) |
| } |
| |
| paths, imports := getPaths(ctx, srcs, root_dir) |
| imports = append(imports, deps.imports...) |
| imports = append(imports, i.properties.Include_dirs...) |
| |
| preprocessCommand := rb.Command().BuiltTool("aidl"). |
| FlagWithOutput("--preprocess ", preprocessed). |
| Flag("--structured") |
| if i.properties.Stability != nil { |
| preprocessCommand.FlagWithArg("--stability ", *i.properties.Stability) |
| } |
| preprocessCommand.FlagForEachInput("-p", deps.preprocessed) |
| preprocessCommand.FlagForEachArg("-I", imports) |
| preprocessCommand.Inputs(paths) |
| name := i.BaseModuleName() |
| if version != "" { |
| name += "/" + version |
| } |
| rb.Build("export_"+name, "export types for "+name) |
| return preprocessed |
| } |
| |
| func (i *aidlInterface) DepsMutator(ctx android.BottomUpMutatorContext) { |
| ctx.AddReverseDependency(ctx.Module(), nil, aidlMetadataSingletonName) |
| } |
| |
| func AidlInterfaceFactory() android.Module { |
| i := &aidlInterface{} |
| i.AddProperties(&i.properties) |
| android.InitAndroidModule(i) |
| android.InitBazelModule(i) |
| android.InitDefaultableModule(i) |
| i.SetDefaultableHook(func(ctx android.DefaultableHookContext) { aidlInterfaceHook(ctx, i) }) |
| return i |
| } |
| |
| type aidlInterfaceAttributes struct { |
| aidlLibraryAttributes |
| Stability *string |
| Versions_with_info []versionWithInfoAttribute |
| Java_config *javaConfigAttributes |
| Cpp_config *cppConfigAttributes |
| Ndk_config *ndkConfigAttributes |
| // Backend_Configs backendConfigAttributes |
| Unstable *bool |
| Frozen *bool |
| } |
| |
| type javaConfigAttributes struct { |
| commonBackendAttributes |
| } |
| type cppConfigAttributes struct { |
| commonNativeBackendAttributes |
| } |
| type ndkConfigAttributes struct { |
| commonNativeBackendAttributes |
| } |
| |
| type commonBackendAttributes struct { |
| Enabled bool |
| Min_sdk_version *string |
| Tags []string |
| } |
| |
| type commonNativeBackendAttributes struct { |
| commonBackendAttributes |
| } |
| |
| type versionWithInfoAttribute struct { |
| Version string |
| // Versions_with_info.Deps in Bazel is analogous to Versions_with_info.Imports in Soong. |
| // Deps is chosen to be consistent with other Bazel rules/macros for AIDL |
| Deps bazel.LabelListAttribute |
| } |
| |
| type aidlLibraryAttributes struct { |
| Srcs bazel.LabelListAttribute |
| Hdrs bazel.LabelListAttribute |
| Deps bazel.LabelListAttribute |
| Strip_import_prefix *string |
| Flags []string |
| } |
| |
| // getBazelLabelListForImports returns a bazel label list converted from |
| // aidl_interface.imports or aidl_interface.versions_with_info.imports prop |
| func getBazelLabelListForImports(ctx android.BazelConversionPathContext, imports []string) bazel.LabelList { |
| type nameAndVersion struct { |
| name string |
| version string |
| } |
| // An aidl_interface with a version designation doesn't correspond to a specific |
| // module, but rather just imforms Soong on which collection of sources to provide |
| // from a particular interface module. |
| // However in Bazel, we will be creating an aidl_library for each version, so we can |
| // depend directly on a "versioned" module. But, we must look up the "unversioned" |
| // module name in BazelLabelForModuleDeps and then re-attach the version information. |
| namesAndVersions := make([]nameAndVersion, len(imports)) |
| names := make([]string, len(imports)) |
| for i, dep := range imports { |
| // Split dep into two parts |
| name, version := parseModuleWithVersion(dep) |
| if version == "" { |
| version = "-latest" |
| } else { |
| version = "-V" + version |
| } |
| namesAndVersions[i] = nameAndVersion{ |
| name: name, |
| version: version, |
| } |
| names[i] = name |
| } |
| // Look up bazel label by name without version |
| bazelLabels := android.BazelLabelForModuleDeps(ctx, names) |
| for i := range bazelLabels.Includes { |
| // Re-attach the version to the name |
| bazelLabels.Includes[i].Label = bazelLabels.Includes[i].Label + namesAndVersions[i].version |
| } |
| return bazelLabels |
| } |
| |
| func (i *aidlInterface) ConvertWithBp2build(ctx android.TopDownMutatorContext) { |
| var javaConfig *javaConfigAttributes |
| var cppConfig *cppConfigAttributes |
| var ndkConfig *ndkConfigAttributes |
| if i.shouldGenerateJavaBackend() { |
| javaConfig = &javaConfigAttributes{} |
| javaConfig.Enabled = true |
| javaConfig.Min_sdk_version = i.minSdkVersion(langJava) |
| javaConfig.Tags = android.ConvertApexAvailableToTagsWithoutTestApexes(ctx, i.properties.Backend.Java.Apex_available) |
| } |
| if i.shouldGenerateCppBackend() { |
| cppConfig = &cppConfigAttributes{} |
| cppConfig.Enabled = true |
| cppConfig.Min_sdk_version = i.minSdkVersion(langCpp) |
| cppConfig.Tags = android.ConvertApexAvailableToTagsWithoutTestApexes(ctx, i.properties.Backend.Cpp.Apex_available) |
| } |
| if i.shouldGenerateNdkBackend() { |
| ndkConfig = &ndkConfigAttributes{} |
| ndkConfig.Enabled = true |
| ndkConfig.Min_sdk_version = i.minSdkVersion(langNdk) |
| ndkConfig.Tags = android.ConvertApexAvailableToTagsWithoutTestApexes(ctx, i.properties.Backend.Ndk.Apex_available) |
| } |
| |
| imports := getBazelLabelListForImports(ctx, i.properties.Imports) |
| |
| var versionsWithInfos []versionWithInfoAttribute |
| |
| if len(i.properties.Versions_with_info) > 0 { |
| for _, versionWithInfo := range i.properties.Versions_with_info { |
| versionedImports := getBazelLabelListForImports(ctx, versionWithInfo.Imports) |
| if !versionedImports.IsEmpty() { |
| versionsWithInfos = append( |
| versionsWithInfos, |
| versionWithInfoAttribute{ |
| Version: versionWithInfo.Version, |
| Deps: bazel.MakeLabelListAttribute(versionedImports), |
| }, |
| ) |
| } else { |
| versionsWithInfos = append( |
| versionsWithInfos, |
| versionWithInfoAttribute{ |
| Version: versionWithInfo.Version, |
| }, |
| ) |
| } |
| } |
| } else if len(i.properties.Versions) > 0 { |
| for _, version := range i.properties.Versions { |
| if !imports.IsEmpty() { |
| versionsWithInfos = append( |
| versionsWithInfos, |
| versionWithInfoAttribute{ |
| Version: version, |
| Deps: bazel.MakeLabelListAttribute(imports), |
| }, |
| ) |
| } else { |
| versionsWithInfos = append( |
| versionsWithInfos, |
| versionWithInfoAttribute{ |
| Version: version, |
| }, |
| ) |
| } |
| } |
| } |
| |
| var deps bazel.LabelListAttribute |
| |
| if len(i.properties.Srcs) > 0 && !imports.IsEmpty() { |
| // imports is only needed for (non-frozen) srcs |
| // frozen verions use imports in versions_with_info |
| deps = bazel.MakeLabelListAttribute(imports) |
| } |
| |
| deps.Append( |
| bazel.MakeLabelListAttribute( |
| android.BazelLabelForModuleDeps( |
| ctx, |
| i.properties.Headers, |
| ), |
| ), |
| ) |
| |
| srcsAttr := bazel.MakeLabelListAttribute(android.BazelLabelForModuleSrc(ctx, i.properties.Srcs)) |
| var stripImportPrefixAttr *string = nil |
| if i.properties.Local_include_dir != "" && !srcsAttr.IsEmpty() { |
| stripImportPrefixAttr = &i.properties.Local_include_dir |
| } |
| |
| attrs := &aidlInterfaceAttributes{ |
| aidlLibraryAttributes: aidlLibraryAttributes{ |
| Srcs: srcsAttr, |
| Flags: i.properties.Flags, |
| Deps: deps, |
| Strip_import_prefix: stripImportPrefixAttr, |
| }, |
| Stability: i.properties.Stability, |
| Versions_with_info: versionsWithInfos, |
| Java_config: javaConfig, |
| Cpp_config: cppConfig, |
| Ndk_config: ndkConfig, |
| Unstable: i.properties.Unstable, |
| Frozen: i.properties.Frozen, |
| } |
| |
| interfaceName := strings.TrimSuffix(i.Name(), "_interface") |
| |
| ctx.CreateBazelTargetModule( |
| bazel.BazelTargetModuleProperties{ |
| Rule_class: "aidl_interface", |
| Bzl_load_location: "//build/bazel/rules/aidl:aidl_interface.bzl", |
| }, |
| android.CommonAttributes{Name: interfaceName}, |
| attrs, |
| ) |
| } |