Merge changes I9f780b20,I53805737,I7373ba10,Ia5c5f65a into sc-dev
* changes: Rename hiddenAPIFlagFileInfo to HiddenAPIInfo Separate input to flag generation from hiddenAPIFlagFileInfo Separate output of flag generation from hiddenAPIFlagFileInfo Separate monolithic hidden API processing from hiddenAPIFlagFileInfo
This commit is contained in:
commit
fa08e19460
|
@ -45,6 +45,7 @@ bootstrap_go_package {
|
|||
"genrule.go",
|
||||
"hiddenapi.go",
|
||||
"hiddenapi_modular.go",
|
||||
"hiddenapi_monolithic.go",
|
||||
"hiddenapi_singleton.go",
|
||||
"jacoco.go",
|
||||
"java.go",
|
||||
|
|
|
@ -133,10 +133,12 @@ type BootclasspathFragmentModule struct {
|
|||
type commonBootclasspathFragment interface {
|
||||
// produceHiddenAPIAllFlagsFile produces the all-flags.csv and intermediate files.
|
||||
//
|
||||
// Updates the supplied flagFileInfo with the paths to the generated files set.
|
||||
produceHiddenAPIAllFlagsFile(ctx android.ModuleContext, contents []hiddenAPIModule, stubJarsByKind map[android.SdkKind]android.Paths, flagFileInfo *hiddenAPIFlagFileInfo)
|
||||
// Updates the supplied hiddenAPIInfo with the paths to the generated files set.
|
||||
produceHiddenAPIAllFlagsFile(ctx android.ModuleContext, contents []hiddenAPIModule, input HiddenAPIFlagInput) *HiddenAPIFlagOutput
|
||||
}
|
||||
|
||||
var _ commonBootclasspathFragment = (*BootclasspathFragmentModule)(nil)
|
||||
|
||||
func bootclasspathFragmentFactory() android.Module {
|
||||
m := &BootclasspathFragmentModule{}
|
||||
m.AddProperties(&m.properties)
|
||||
|
@ -386,7 +388,7 @@ func (b *BootclasspathFragmentModule) GenerateAndroidBuildActions(ctx android.Mo
|
|||
})
|
||||
|
||||
// Perform hidden API processing.
|
||||
hiddenAPIInfo := b.generateHiddenAPIBuildActions(ctx, contents)
|
||||
hiddenAPIFlagOutput := b.generateHiddenAPIBuildActions(ctx, contents)
|
||||
|
||||
// Verify that the image_name specified on a bootclasspath_fragment is valid even if this is a
|
||||
// prebuilt which will not use the image config.
|
||||
|
@ -395,20 +397,20 @@ func (b *BootclasspathFragmentModule) GenerateAndroidBuildActions(ctx android.Mo
|
|||
// A prebuilt fragment cannot contribute to the apex.
|
||||
if !android.IsModulePrebuilt(ctx.Module()) {
|
||||
// Provide the apex content info.
|
||||
b.provideApexContentInfo(ctx, imageConfig, contents, hiddenAPIInfo)
|
||||
b.provideApexContentInfo(ctx, imageConfig, contents, hiddenAPIFlagOutput)
|
||||
}
|
||||
}
|
||||
|
||||
// provideApexContentInfo creates, initializes and stores the apex content info for use by other
|
||||
// modules.
|
||||
func (b *BootclasspathFragmentModule) provideApexContentInfo(ctx android.ModuleContext, imageConfig *bootImageConfig, contents []android.Module, hiddenAPIInfo *hiddenAPIFlagFileInfo) {
|
||||
func (b *BootclasspathFragmentModule) provideApexContentInfo(ctx android.ModuleContext, imageConfig *bootImageConfig, contents []android.Module, hiddenAPIFlagOutput *HiddenAPIFlagOutput) {
|
||||
// Construct the apex content info from the config.
|
||||
info := BootclasspathFragmentApexContentInfo{
|
||||
imageConfig: imageConfig,
|
||||
}
|
||||
|
||||
// Populate the apex content info with paths to the dex jars.
|
||||
b.populateApexContentInfoDexJars(ctx, &info, contents, hiddenAPIInfo)
|
||||
b.populateApexContentInfoDexJars(ctx, &info, contents, hiddenAPIFlagOutput)
|
||||
|
||||
if !SkipDexpreoptBootJars(ctx) {
|
||||
// Force the GlobalSoongConfig to be created and cached for use by the dex_bootjars
|
||||
|
@ -425,12 +427,12 @@ func (b *BootclasspathFragmentModule) provideApexContentInfo(ctx android.ModuleC
|
|||
|
||||
// populateApexContentInfoDexJars adds paths to the dex jars provided by this fragment to the
|
||||
// apex content info.
|
||||
func (b *BootclasspathFragmentModule) populateApexContentInfoDexJars(ctx android.ModuleContext, info *BootclasspathFragmentApexContentInfo, contents []android.Module, hiddenAPIInfo *hiddenAPIFlagFileInfo) {
|
||||
func (b *BootclasspathFragmentModule) populateApexContentInfoDexJars(ctx android.ModuleContext, info *BootclasspathFragmentApexContentInfo, contents []android.Module, hiddenAPIFlagOutput *HiddenAPIFlagOutput) {
|
||||
|
||||
info.contentModuleDexJarPaths = map[string]android.Path{}
|
||||
if hiddenAPIInfo != nil {
|
||||
if hiddenAPIFlagOutput != nil {
|
||||
// Hidden API encoding has been performed.
|
||||
flags := hiddenAPIInfo.AllFlagsPaths[0]
|
||||
flags := hiddenAPIFlagOutput.AllFlagsPath
|
||||
for _, m := range contents {
|
||||
h := m.(hiddenAPIModule)
|
||||
unencodedDex := h.bootDexJar()
|
||||
|
@ -527,7 +529,7 @@ func (b *BootclasspathFragmentModule) canPerformHiddenAPIProcessing(ctx android.
|
|||
}
|
||||
|
||||
// generateHiddenAPIBuildActions generates all the hidden API related build rules.
|
||||
func (b *BootclasspathFragmentModule) generateHiddenAPIBuildActions(ctx android.ModuleContext, contents []android.Module) *hiddenAPIFlagFileInfo {
|
||||
func (b *BootclasspathFragmentModule) generateHiddenAPIBuildActions(ctx android.ModuleContext, contents []android.Module) *HiddenAPIFlagOutput {
|
||||
|
||||
// A temporary workaround to avoid existing bootclasspath_fragments that do not provide the
|
||||
// appropriate information needed for hidden API processing breaking the build.
|
||||
|
@ -536,42 +538,61 @@ func (b *BootclasspathFragmentModule) generateHiddenAPIBuildActions(ctx android.
|
|||
return nil
|
||||
}
|
||||
|
||||
// Convert the kind specific lists of modules into kind specific lists of jars.
|
||||
stubJarsByKind := hiddenAPIGatherStubLibDexJarPaths(ctx, contents)
|
||||
// Create hidden API input structure.
|
||||
input := b.createHiddenAPIFlagInput(ctx, contents)
|
||||
|
||||
// Performing hidden API processing without stubs is not supported and it is unlikely to ever be
|
||||
// required as the whole point of adding something to the bootclasspath fragment is to add it to
|
||||
// the bootclasspath in order to be used by something else in the system. Without any stubs it
|
||||
// cannot do that.
|
||||
if len(stubJarsByKind) == 0 {
|
||||
if len(input.StubDexJarsByKind) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Store the information for use by other modules.
|
||||
bootclasspathApiInfo := bootclasspathApiInfo{stubJarsByKind: stubJarsByKind}
|
||||
bootclasspathApiInfo := bootclasspathApiInfo{stubJarsByKind: input.StubDexJarsByKind}
|
||||
ctx.SetProvider(bootclasspathApiInfoProvider, bootclasspathApiInfo)
|
||||
|
||||
// Resolve the properties to paths.
|
||||
flagFileInfo := b.properties.Hidden_api.hiddenAPIFlagFileInfo(ctx)
|
||||
|
||||
hiddenAPIModules := gatherHiddenAPIModuleFromContents(ctx, contents)
|
||||
|
||||
// Delegate the production of the hidden API all flags file to a module type specific method.
|
||||
// Delegate the production of the hidden API all-flags.csv file to a module type specific method.
|
||||
common := ctx.Module().(commonBootclasspathFragment)
|
||||
common.produceHiddenAPIAllFlagsFile(ctx, hiddenAPIModules, stubJarsByKind, &flagFileInfo)
|
||||
output := common.produceHiddenAPIAllFlagsFile(ctx, hiddenAPIModules, input)
|
||||
|
||||
// Store the information for use by platform_bootclasspath.
|
||||
ctx.SetProvider(hiddenAPIFlagFileInfoProvider, flagFileInfo)
|
||||
// Initialize a HiddenAPIInfo structure and provide it for use by other modules.
|
||||
hiddenAPIInfo := HiddenAPIInfo{
|
||||
// The monolithic hidden API processing needs access to the flag files from all the fragments.
|
||||
FlagFilesByCategory: input.FlagFilesByCategory,
|
||||
|
||||
return &flagFileInfo
|
||||
// The monolithic hidden API processing also needs access to all the output files produced by
|
||||
// hidden API processing of this fragment.
|
||||
HiddenAPIFlagOutput: *output,
|
||||
}
|
||||
ctx.SetProvider(HiddenAPIInfoProvider, hiddenAPIInfo)
|
||||
|
||||
return output
|
||||
}
|
||||
|
||||
// createHiddenAPIFlagInput creates a HiddenAPIFlagInput struct and initializes it with information derived
|
||||
// from the properties on this module and its dependencies.
|
||||
func (b *BootclasspathFragmentModule) createHiddenAPIFlagInput(ctx android.ModuleContext, contents []android.Module) HiddenAPIFlagInput {
|
||||
input := newHiddenAPIFlagInput()
|
||||
|
||||
// Update the input structure with information obtained from the stub libraries.
|
||||
input.gatherStubLibInfo(ctx, contents)
|
||||
|
||||
// Populate with flag file paths from the properties.
|
||||
input.extractFlagFilesFromProperties(ctx, &b.properties.Hidden_api)
|
||||
|
||||
return input
|
||||
}
|
||||
|
||||
// produceHiddenAPIAllFlagsFile produces the hidden API all-flags.csv file (and supporting files)
|
||||
// for the fragment.
|
||||
func (b *BootclasspathFragmentModule) produceHiddenAPIAllFlagsFile(ctx android.ModuleContext, contents []hiddenAPIModule, stubJarsByKind map[android.SdkKind]android.Paths, flagFileInfo *hiddenAPIFlagFileInfo) {
|
||||
// Generate the rules to create the hidden API flags and update the supplied flagFileInfo with the
|
||||
func (b *BootclasspathFragmentModule) produceHiddenAPIAllFlagsFile(ctx android.ModuleContext, contents []hiddenAPIModule, input HiddenAPIFlagInput) *HiddenAPIFlagOutput {
|
||||
// Generate the rules to create the hidden API flags and update the supplied hiddenAPIInfo with the
|
||||
// paths to the created files.
|
||||
hiddenAPIGenerateAllFlagsForBootclasspathFragment(ctx, contents, stubJarsByKind, flagFileInfo)
|
||||
return hiddenAPIGenerateAllFlagsForBootclasspathFragment(ctx, contents, input)
|
||||
}
|
||||
|
||||
// generateBootImageBuildActions generates ninja rules to create the boot image if required for this
|
||||
|
@ -651,7 +672,7 @@ type bootclasspathFragmentSdkMemberProperties struct {
|
|||
Core_platform_stub_libs []string
|
||||
|
||||
// Flag files by *hiddenAPIFlagFileCategory
|
||||
Flag_files_by_category map[*hiddenAPIFlagFileCategory]android.Paths
|
||||
Flag_files_by_category FlagFilesByCategory
|
||||
|
||||
// The path to the generated stub-flags.csv file.
|
||||
Stub_flags_path android.OptionalPath
|
||||
|
@ -669,34 +690,23 @@ type bootclasspathFragmentSdkMemberProperties struct {
|
|||
All_flags_path android.OptionalPath
|
||||
}
|
||||
|
||||
func pathsToOptionalPath(paths android.Paths) android.OptionalPath {
|
||||
switch len(paths) {
|
||||
case 0:
|
||||
return android.OptionalPath{}
|
||||
case 1:
|
||||
return android.OptionalPathForPath(paths[0])
|
||||
default:
|
||||
panic(fmt.Errorf("expected 0 or 1 paths, found %q", paths))
|
||||
}
|
||||
}
|
||||
|
||||
func (b *bootclasspathFragmentSdkMemberProperties) PopulateFromVariant(ctx android.SdkMemberContext, variant android.Module) {
|
||||
module := variant.(*BootclasspathFragmentModule)
|
||||
|
||||
b.Image_name = module.properties.Image_name
|
||||
b.Contents = module.properties.Contents
|
||||
|
||||
// Get the flag file information from the module.
|
||||
// Get the hidden API information from the module.
|
||||
mctx := ctx.SdkModuleContext()
|
||||
flagFileInfo := mctx.OtherModuleProvider(module, hiddenAPIFlagFileInfoProvider).(hiddenAPIFlagFileInfo)
|
||||
b.Flag_files_by_category = flagFileInfo.categoryToPaths
|
||||
hiddenAPIInfo := mctx.OtherModuleProvider(module, HiddenAPIInfoProvider).(HiddenAPIInfo)
|
||||
b.Flag_files_by_category = hiddenAPIInfo.FlagFilesByCategory
|
||||
|
||||
// Copy all the generated file paths.
|
||||
b.Stub_flags_path = pathsToOptionalPath(flagFileInfo.StubFlagsPaths)
|
||||
b.Annotation_flags_path = pathsToOptionalPath(flagFileInfo.AnnotationFlagsPaths)
|
||||
b.Metadata_path = pathsToOptionalPath(flagFileInfo.MetadataPaths)
|
||||
b.Index_path = pathsToOptionalPath(flagFileInfo.IndexPaths)
|
||||
b.All_flags_path = pathsToOptionalPath(flagFileInfo.AllFlagsPaths)
|
||||
b.Stub_flags_path = android.OptionalPathForPath(hiddenAPIInfo.StubFlagsPath)
|
||||
b.Annotation_flags_path = android.OptionalPathForPath(hiddenAPIInfo.AnnotationFlagsPath)
|
||||
b.Metadata_path = android.OptionalPathForPath(hiddenAPIInfo.MetadataPath)
|
||||
b.Index_path = android.OptionalPathForPath(hiddenAPIInfo.IndexPath)
|
||||
b.All_flags_path = android.OptionalPathForPath(hiddenAPIInfo.AllFlagsPath)
|
||||
|
||||
// Copy stub_libs properties.
|
||||
b.Stub_libs = module.properties.Api.Stub_libs
|
||||
|
@ -806,20 +816,24 @@ func (module *prebuiltBootclasspathFragmentModule) Name() string {
|
|||
|
||||
// produceHiddenAPIAllFlagsFile returns a path to the prebuilt all-flags.csv or nil if none is
|
||||
// specified.
|
||||
func (module *prebuiltBootclasspathFragmentModule) produceHiddenAPIAllFlagsFile(ctx android.ModuleContext, contents []hiddenAPIModule, stubJarsByKind map[android.SdkKind]android.Paths, flagFileInfo *hiddenAPIFlagFileInfo) {
|
||||
pathsForOptionalSrc := func(src *string) android.Paths {
|
||||
func (module *prebuiltBootclasspathFragmentModule) produceHiddenAPIAllFlagsFile(ctx android.ModuleContext, contents []hiddenAPIModule, _ HiddenAPIFlagInput) *HiddenAPIFlagOutput {
|
||||
pathForOptionalSrc := func(src *string) android.Path {
|
||||
if src == nil {
|
||||
// TODO(b/179354495): Fail if this is not provided once prebuilts have been updated.
|
||||
return nil
|
||||
}
|
||||
return android.Paths{android.PathForModuleSrc(ctx, *src)}
|
||||
return android.PathForModuleSrc(ctx, *src)
|
||||
}
|
||||
|
||||
flagFileInfo.StubFlagsPaths = pathsForOptionalSrc(module.prebuiltProperties.Hidden_api.Stub_flags)
|
||||
flagFileInfo.AnnotationFlagsPaths = pathsForOptionalSrc(module.prebuiltProperties.Hidden_api.Annotation_flags)
|
||||
flagFileInfo.MetadataPaths = pathsForOptionalSrc(module.prebuiltProperties.Hidden_api.Metadata)
|
||||
flagFileInfo.IndexPaths = pathsForOptionalSrc(module.prebuiltProperties.Hidden_api.Index)
|
||||
flagFileInfo.AllFlagsPaths = pathsForOptionalSrc(module.prebuiltProperties.Hidden_api.All_flags)
|
||||
output := HiddenAPIFlagOutput{
|
||||
StubFlagsPath: pathForOptionalSrc(module.prebuiltProperties.Hidden_api.Stub_flags),
|
||||
AnnotationFlagsPath: pathForOptionalSrc(module.prebuiltProperties.Hidden_api.Annotation_flags),
|
||||
MetadataPath: pathForOptionalSrc(module.prebuiltProperties.Hidden_api.Metadata),
|
||||
IndexPath: pathForOptionalSrc(module.prebuiltProperties.Hidden_api.Index),
|
||||
AllFlagsPath: pathForOptionalSrc(module.prebuiltProperties.Hidden_api.All_flags),
|
||||
}
|
||||
|
||||
return &output
|
||||
}
|
||||
|
||||
var _ commonBootclasspathFragment = (*prebuiltBootclasspathFragmentModule)(nil)
|
||||
|
|
|
@ -127,42 +127,6 @@ func hiddenAPIAddStubLibDependencies(ctx android.BottomUpMutatorContext, sdkKind
|
|||
}
|
||||
}
|
||||
|
||||
// hiddenAPIGatherStubLibDexJarPaths gathers the paths to the dex jars from the dependencies added
|
||||
// in hiddenAPIAddStubLibDependencies.
|
||||
func hiddenAPIGatherStubLibDexJarPaths(ctx android.ModuleContext, contents []android.Module) map[android.SdkKind]android.Paths {
|
||||
m := map[android.SdkKind]android.Paths{}
|
||||
|
||||
// If the contents includes any java_sdk_library modules then add them to the stubs.
|
||||
for _, module := range contents {
|
||||
if _, ok := module.(SdkLibraryDependency); ok {
|
||||
for _, kind := range []android.SdkKind{android.SdkPublic, android.SdkSystem, android.SdkTest} {
|
||||
dexJar := hiddenAPIRetrieveDexJarBuildPath(ctx, module, kind)
|
||||
if dexJar != nil {
|
||||
m[kind] = append(m[kind], dexJar)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ctx.VisitDirectDepsIf(isActiveModule, func(module android.Module) {
|
||||
tag := ctx.OtherModuleDependencyTag(module)
|
||||
if hiddenAPIStubsTag, ok := tag.(hiddenAPIStubsDependencyTag); ok {
|
||||
kind := hiddenAPIStubsTag.sdkKind
|
||||
dexJar := hiddenAPIRetrieveDexJarBuildPath(ctx, module, kind)
|
||||
if dexJar != nil {
|
||||
m[kind] = append(m[kind], dexJar)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// Normalize the paths, i.e. remove duplicates and sort.
|
||||
for k, v := range m {
|
||||
m[k] = android.SortedUniquePaths(v)
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
// hiddenAPIRetrieveDexJarBuildPath retrieves the DexJarBuildPath from the specified module, if
|
||||
// available, or reports an error.
|
||||
func hiddenAPIRetrieveDexJarBuildPath(ctx android.ModuleContext, module android.Module, kind android.SdkKind) android.Path {
|
||||
|
@ -193,7 +157,7 @@ var sdkKindToHiddenapiListOption = map[android.SdkKind]string{
|
|||
//
|
||||
// The rule is initialized but not built so that the caller can modify it and select an appropriate
|
||||
// name.
|
||||
func ruleToGenerateHiddenAPIStubFlagsFile(ctx android.BuilderContext, outputPath android.WritablePath, bootDexJars android.Paths, sdkKindToPathList map[android.SdkKind]android.Paths) *android.RuleBuilder {
|
||||
func ruleToGenerateHiddenAPIStubFlagsFile(ctx android.BuilderContext, outputPath android.WritablePath, bootDexJars android.Paths, input HiddenAPIFlagInput) *android.RuleBuilder {
|
||||
// Singleton rule which applies hiddenapi on all boot class path dex files.
|
||||
rule := android.NewRuleBuilder(pctx, ctx)
|
||||
|
||||
|
@ -206,7 +170,7 @@ func ruleToGenerateHiddenAPIStubFlagsFile(ctx android.BuilderContext, outputPath
|
|||
|
||||
// Iterate over the sdk kinds in a fixed order.
|
||||
for _, sdkKind := range hiddenAPIRelevantSdkKinds {
|
||||
paths := sdkKindToPathList[sdkKind]
|
||||
paths := input.StubDexJarsByKind[sdkKind]
|
||||
if len(paths) > 0 {
|
||||
option := sdkKindToHiddenapiListOption[sdkKind]
|
||||
command.FlagWithInputList("--"+option+"=", paths, ":")
|
||||
|
@ -260,15 +224,6 @@ type HiddenAPIFlagFileProperties struct {
|
|||
Unsupported_packages []string `android:"path"`
|
||||
}
|
||||
|
||||
func (p *HiddenAPIFlagFileProperties) hiddenAPIFlagFileInfo(ctx android.ModuleContext) hiddenAPIFlagFileInfo {
|
||||
info := hiddenAPIFlagFileInfo{categoryToPaths: map[*hiddenAPIFlagFileCategory]android.Paths{}}
|
||||
for _, category := range hiddenAPIFlagFileCategories {
|
||||
paths := android.PathsForModuleSrc(ctx, category.propertyValueReader(p))
|
||||
info.categoryToPaths[category] = paths
|
||||
}
|
||||
return info
|
||||
}
|
||||
|
||||
type hiddenAPIFlagFileCategory struct {
|
||||
// propertyName is the name of the property for this category.
|
||||
propertyName string
|
||||
|
@ -365,45 +320,144 @@ var hiddenAPIFlagFileCategories = []*hiddenAPIFlagFileCategory{
|
|||
},
|
||||
}
|
||||
|
||||
// hiddenAPIFlagFileInfo contains paths resolved from HiddenAPIFlagFileProperties and also generated
|
||||
// by hidden API processing.
|
||||
//
|
||||
// This is used both for an individual bootclasspath_fragment to provide it to other modules and
|
||||
// for a module to collate the files from the fragments it depends upon. That is why the fields are
|
||||
// all Paths even though they are initialized with a single path.
|
||||
type hiddenAPIFlagFileInfo struct {
|
||||
// categoryToPaths maps from the flag file category to the paths containing information for that
|
||||
// category.
|
||||
categoryToPaths map[*hiddenAPIFlagFileCategory]android.Paths
|
||||
// FlagFilesByCategory maps a hiddenAPIFlagFileCategory to the paths to the files in that category.
|
||||
type FlagFilesByCategory map[*hiddenAPIFlagFileCategory]android.Paths
|
||||
|
||||
// The paths to the generated stub-flags.csv files.
|
||||
StubFlagsPaths android.Paths
|
||||
|
||||
// The paths to the generated annotation-flags.csv files.
|
||||
AnnotationFlagsPaths android.Paths
|
||||
|
||||
// The paths to the generated metadata.csv files.
|
||||
MetadataPaths android.Paths
|
||||
|
||||
// The paths to the generated index.csv files.
|
||||
IndexPaths android.Paths
|
||||
|
||||
// The paths to the generated all-flags.csv files.
|
||||
AllFlagsPaths android.Paths
|
||||
}
|
||||
|
||||
func (i *hiddenAPIFlagFileInfo) append(other hiddenAPIFlagFileInfo) {
|
||||
// append appends the supplied flags files to the corresponding category in this map.
|
||||
func (s FlagFilesByCategory) append(other FlagFilesByCategory) {
|
||||
for _, category := range hiddenAPIFlagFileCategories {
|
||||
i.categoryToPaths[category] = append(i.categoryToPaths[category], other.categoryToPaths[category]...)
|
||||
s[category] = append(s[category], other[category]...)
|
||||
}
|
||||
i.StubFlagsPaths = append(i.StubFlagsPaths, other.StubFlagsPaths...)
|
||||
i.AnnotationFlagsPaths = append(i.AnnotationFlagsPaths, other.AnnotationFlagsPaths...)
|
||||
i.MetadataPaths = append(i.MetadataPaths, other.MetadataPaths...)
|
||||
i.IndexPaths = append(i.IndexPaths, other.IndexPaths...)
|
||||
i.AllFlagsPaths = append(i.AllFlagsPaths, other.AllFlagsPaths...)
|
||||
}
|
||||
|
||||
var hiddenAPIFlagFileInfoProvider = blueprint.NewProvider(hiddenAPIFlagFileInfo{})
|
||||
// dedup removes duplicates in the flag files, while maintaining the order in which they were
|
||||
// appended.
|
||||
func (s FlagFilesByCategory) dedup() {
|
||||
for category, paths := range s {
|
||||
s[category] = android.FirstUniquePaths(paths)
|
||||
}
|
||||
}
|
||||
|
||||
// HiddenAPIInfo contains information provided by the hidden API processing.
|
||||
//
|
||||
// That includes paths resolved from HiddenAPIFlagFileProperties and also generated by hidden API
|
||||
// processing.
|
||||
type HiddenAPIInfo struct {
|
||||
// FlagFilesByCategory maps from the flag file category to the paths containing information for
|
||||
// that category.
|
||||
FlagFilesByCategory FlagFilesByCategory
|
||||
|
||||
// The output from the hidden API processing needs to be made available to other modules.
|
||||
HiddenAPIFlagOutput
|
||||
}
|
||||
|
||||
var HiddenAPIInfoProvider = blueprint.NewProvider(HiddenAPIInfo{})
|
||||
|
||||
// StubDexJarsByKind maps an android.SdkKind to the paths to stub dex jars appropriate for that
|
||||
// level. See hiddenAPIRelevantSdkKinds for a list of the acceptable android.SdkKind values.
|
||||
type StubDexJarsByKind map[android.SdkKind]android.Paths
|
||||
|
||||
// append appends the supplied kind specific stub dex jar pargs to the corresponding kind in this
|
||||
// map.
|
||||
func (s StubDexJarsByKind) append(other StubDexJarsByKind) {
|
||||
for _, kind := range hiddenAPIRelevantSdkKinds {
|
||||
s[kind] = append(s[kind], other[kind]...)
|
||||
}
|
||||
}
|
||||
|
||||
// dedupAndSort removes duplicates in the stub dex jar paths and sorts them into a consistent and
|
||||
// deterministic order.
|
||||
func (s StubDexJarsByKind) dedupAndSort() {
|
||||
for kind, paths := range s {
|
||||
s[kind] = android.SortedUniquePaths(paths)
|
||||
}
|
||||
}
|
||||
|
||||
// HiddenAPIFlagInput encapsulates information obtained from a module and its dependencies that are
|
||||
// needed for hidden API flag generation.
|
||||
type HiddenAPIFlagInput struct {
|
||||
// FlagFilesByCategory contains the flag files that override the initial flags that are derived
|
||||
// from the stub dex files.
|
||||
FlagFilesByCategory FlagFilesByCategory
|
||||
|
||||
// StubDexJarsByKind contains the stub dex jars for different android.SdkKind and which determine
|
||||
// the initial flags for each dex member.
|
||||
StubDexJarsByKind StubDexJarsByKind
|
||||
}
|
||||
|
||||
// newHiddenAPIFlagInput creates a new initialize HiddenAPIFlagInput struct.
|
||||
func newHiddenAPIFlagInput() HiddenAPIFlagInput {
|
||||
input := HiddenAPIFlagInput{
|
||||
FlagFilesByCategory: FlagFilesByCategory{},
|
||||
StubDexJarsByKind: StubDexJarsByKind{},
|
||||
}
|
||||
|
||||
return input
|
||||
}
|
||||
|
||||
// gatherStubLibInfo gathers information from the stub libs needed by hidden API processing from the
|
||||
// dependencies added in hiddenAPIAddStubLibDependencies.
|
||||
//
|
||||
// That includes paths to the stub dex jars as well as paths to the *removed.txt files.
|
||||
func (i *HiddenAPIFlagInput) gatherStubLibInfo(ctx android.ModuleContext, contents []android.Module) {
|
||||
addFromModule := func(ctx android.ModuleContext, module android.Module, kind android.SdkKind) {
|
||||
dexJar := hiddenAPIRetrieveDexJarBuildPath(ctx, module, kind)
|
||||
if dexJar != nil {
|
||||
i.StubDexJarsByKind[kind] = append(i.StubDexJarsByKind[kind], dexJar)
|
||||
}
|
||||
}
|
||||
|
||||
// If the contents includes any java_sdk_library modules then add them to the stubs.
|
||||
for _, module := range contents {
|
||||
if _, ok := module.(SdkLibraryDependency); ok {
|
||||
// Add information for every possible kind needed by hidden API. SdkCorePlatform is not used
|
||||
// as the java_sdk_library does not have special support for core_platform API, instead it is
|
||||
// implemented as a customized form of SdkPublic.
|
||||
for _, kind := range []android.SdkKind{android.SdkPublic, android.SdkSystem, android.SdkTest} {
|
||||
addFromModule(ctx, module, kind)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ctx.VisitDirectDepsIf(isActiveModule, func(module android.Module) {
|
||||
tag := ctx.OtherModuleDependencyTag(module)
|
||||
if hiddenAPIStubsTag, ok := tag.(hiddenAPIStubsDependencyTag); ok {
|
||||
kind := hiddenAPIStubsTag.sdkKind
|
||||
addFromModule(ctx, module, kind)
|
||||
}
|
||||
})
|
||||
|
||||
// Normalize the paths, i.e. remove duplicates and sort.
|
||||
i.StubDexJarsByKind.dedupAndSort()
|
||||
}
|
||||
|
||||
// extractFlagFilesFromProperties extracts the paths to flag files that are specified in the
|
||||
// supplied properties and stores them in this struct.
|
||||
func (i *HiddenAPIFlagInput) extractFlagFilesFromProperties(ctx android.ModuleContext, p *HiddenAPIFlagFileProperties) {
|
||||
for _, category := range hiddenAPIFlagFileCategories {
|
||||
paths := android.PathsForModuleSrc(ctx, category.propertyValueReader(p))
|
||||
i.FlagFilesByCategory[category] = paths
|
||||
}
|
||||
}
|
||||
|
||||
// HiddenAPIFlagOutput contains paths to output files from the hidden API flag generation for a
|
||||
// bootclasspath_fragment module.
|
||||
type HiddenAPIFlagOutput struct {
|
||||
// The path to the generated stub-flags.csv file.
|
||||
StubFlagsPath android.Path
|
||||
|
||||
// The path to the generated annotation-flags.csv file.
|
||||
AnnotationFlagsPath android.Path
|
||||
|
||||
// The path to the generated metadata.csv file.
|
||||
MetadataPath android.Path
|
||||
|
||||
// The path to the generated index.csv file.
|
||||
IndexPath android.Path
|
||||
|
||||
// The path to the generated all-flags.csv file.
|
||||
AllFlagsPath android.Path
|
||||
}
|
||||
|
||||
// pathForValidation creates a path of the same type as the supplied type but with a name of
|
||||
// <path>.valid.
|
||||
|
@ -426,16 +480,16 @@ func pathForValidation(ctx android.PathContext, path android.WritablePath) andro
|
|||
// annotationFlags is the path to the annotation flags file generated from annotation information
|
||||
// in each module.
|
||||
//
|
||||
// flagFileInfo is a struct containing paths to files that augment the information provided by
|
||||
// hiddenAPIInfo is a struct containing paths to files that augment the information provided by
|
||||
// the annotationFlags.
|
||||
func buildRuleToGenerateHiddenApiFlags(ctx android.BuilderContext, name, desc string, outputPath android.WritablePath, baseFlagsPath android.Path, annotationFlags android.Path, flagFileInfo *hiddenAPIFlagFileInfo) {
|
||||
func buildRuleToGenerateHiddenApiFlags(ctx android.BuilderContext, name, desc string, outputPath android.WritablePath, baseFlagsPath android.Path, annotationFlags android.Path, flagFilesByCategory FlagFilesByCategory, allFlagsPaths android.Paths) {
|
||||
|
||||
// The file which is used to record that the flags file is valid.
|
||||
var validFile android.WritablePath
|
||||
|
||||
// If there are flag files that have been generated by fragments on which this depends then use
|
||||
// them to validate the flag file generated by the rules created by this method.
|
||||
if allFlagsPaths := flagFileInfo.AllFlagsPaths; len(allFlagsPaths) > 0 {
|
||||
if len(allFlagsPaths) > 0 {
|
||||
// The flags file generated by the rule created by this method needs to be validated to ensure
|
||||
// that it is consistent with the flag files generated by the individual fragments.
|
||||
|
||||
|
@ -463,7 +517,7 @@ func buildRuleToGenerateHiddenApiFlags(ctx android.BuilderContext, name, desc st
|
|||
|
||||
// Add the options for the different categories of flag files.
|
||||
for _, category := range hiddenAPIFlagFileCategories {
|
||||
paths := flagFileInfo.categoryToPaths[category]
|
||||
paths := flagFilesByCategory[category]
|
||||
for _, path := range paths {
|
||||
category.commandMutator(command, path)
|
||||
}
|
||||
|
@ -496,13 +550,15 @@ func buildRuleToGenerateHiddenApiFlags(ctx android.BuilderContext, name, desc st
|
|||
// * metadata.csv
|
||||
// * index.csv
|
||||
// * all-flags.csv
|
||||
func hiddenAPIGenerateAllFlagsForBootclasspathFragment(ctx android.ModuleContext, contents []hiddenAPIModule, stubJarsByKind map[android.SdkKind]android.Paths, flagFileInfo *hiddenAPIFlagFileInfo) {
|
||||
func hiddenAPIGenerateAllFlagsForBootclasspathFragment(ctx android.ModuleContext, contents []hiddenAPIModule, input HiddenAPIFlagInput) *HiddenAPIFlagOutput {
|
||||
hiddenApiSubDir := "modular-hiddenapi"
|
||||
|
||||
// Generate the stub-flags.csv.
|
||||
// Gather the dex files for the boot libraries provided by this fragment.
|
||||
bootDexJars := extractBootDexJarsFromHiddenAPIModules(ctx, contents)
|
||||
|
||||
// Generate the stub-flags.csv.
|
||||
stubFlagsCSV := android.PathForModuleOut(ctx, hiddenApiSubDir, "stub-flags.csv")
|
||||
rule := ruleToGenerateHiddenAPIStubFlagsFile(ctx, stubFlagsCSV, bootDexJars, stubJarsByKind)
|
||||
rule := ruleToGenerateHiddenAPIStubFlagsFile(ctx, stubFlagsCSV, bootDexJars, input)
|
||||
rule.Build("modularHiddenAPIStubFlagsFile", "modular hiddenapi stub flags")
|
||||
|
||||
// Extract the classes jars from the contents.
|
||||
|
@ -520,7 +576,7 @@ func hiddenAPIGenerateAllFlagsForBootclasspathFragment(ctx android.ModuleContext
|
|||
indexCSV := android.PathForModuleOut(ctx, hiddenApiSubDir, "index.csv")
|
||||
buildRuleToGenerateIndex(ctx, "modular hiddenapi index", classesJars, indexCSV)
|
||||
|
||||
// Removed APIs need to be marked and in order to do that the flagFileInfo needs to specify files
|
||||
// Removed APIs need to be marked and in order to do that the hiddenAPIInfo needs to specify files
|
||||
// containing dex signatures of all the removed APIs. In the monolithic files that is done by
|
||||
// manually combining all the removed.txt files for each API and then converting them to dex
|
||||
// signatures, see the combined-removed-dex module. That will all be done automatically in future.
|
||||
|
@ -530,14 +586,17 @@ func hiddenAPIGenerateAllFlagsForBootclasspathFragment(ctx android.ModuleContext
|
|||
// Generate the all-flags.csv which are the flags that will, in future, be encoded into the dex
|
||||
// files.
|
||||
outputPath := android.PathForModuleOut(ctx, hiddenApiSubDir, "all-flags.csv")
|
||||
buildRuleToGenerateHiddenApiFlags(ctx, "modularHiddenApiAllFlags", "modular hiddenapi all flags", outputPath, stubFlagsCSV, annotationFlagsCSV, flagFileInfo)
|
||||
buildRuleToGenerateHiddenApiFlags(ctx, "modularHiddenApiAllFlags", "modular hiddenapi all flags", outputPath, stubFlagsCSV, annotationFlagsCSV, input.FlagFilesByCategory, nil)
|
||||
|
||||
// Store the paths in the info for use by other modules and sdk snapshot generation.
|
||||
flagFileInfo.StubFlagsPaths = android.Paths{stubFlagsCSV}
|
||||
flagFileInfo.AnnotationFlagsPaths = android.Paths{annotationFlagsCSV}
|
||||
flagFileInfo.MetadataPaths = android.Paths{metadataCSV}
|
||||
flagFileInfo.IndexPaths = android.Paths{indexCSV}
|
||||
flagFileInfo.AllFlagsPaths = android.Paths{outputPath}
|
||||
output := HiddenAPIFlagOutput{
|
||||
StubFlagsPath: stubFlagsCSV,
|
||||
AnnotationFlagsPath: annotationFlagsCSV,
|
||||
MetadataPath: metadataCSV,
|
||||
IndexPath: indexCSV,
|
||||
AllFlagsPath: outputPath,
|
||||
}
|
||||
return &output
|
||||
}
|
||||
|
||||
// gatherHiddenAPIModuleFromContents gathers the hiddenAPIModule from the supplied contents.
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
// Copyright (C) 2021 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 java
|
||||
|
||||
import (
|
||||
"android/soong/android"
|
||||
"github.com/google/blueprint"
|
||||
)
|
||||
|
||||
// MonolithicHiddenAPIInfo contains information needed/provided by the hidden API generation of the
|
||||
// monolithic hidden API files.
|
||||
//
|
||||
// Each list of paths includes all the equivalent paths from each of the bootclasspath_fragment
|
||||
// modules that contribute to the platform-bootclasspath.
|
||||
type MonolithicHiddenAPIInfo struct {
|
||||
// FlagsFilesByCategory maps from the flag file category to the paths containing information for
|
||||
// that category.
|
||||
FlagsFilesByCategory FlagFilesByCategory
|
||||
|
||||
// The paths to the generated stub-flags.csv files.
|
||||
StubFlagsPaths android.Paths
|
||||
|
||||
// The paths to the generated annotation-flags.csv files.
|
||||
AnnotationFlagsPaths android.Paths
|
||||
|
||||
// The paths to the generated metadata.csv files.
|
||||
MetadataPaths android.Paths
|
||||
|
||||
// The paths to the generated index.csv files.
|
||||
IndexPaths android.Paths
|
||||
|
||||
// The paths to the generated all-flags.csv files.
|
||||
AllFlagsPaths android.Paths
|
||||
}
|
||||
|
||||
// newMonolithicHiddenAPIInfo creates a new MonolithicHiddenAPIInfo from the flagFilesByCategory
|
||||
// plus information provided by each of the fragments.
|
||||
func newMonolithicHiddenAPIInfo(ctx android.ModuleContext, flagFilesByCategory FlagFilesByCategory, fragments []android.Module) MonolithicHiddenAPIInfo {
|
||||
monolithicInfo := MonolithicHiddenAPIInfo{}
|
||||
|
||||
monolithicInfo.FlagsFilesByCategory = flagFilesByCategory
|
||||
|
||||
// Merge all the information from the fragments. The fragments form a DAG so it is possible that
|
||||
// this will introduce duplicates so they will be resolved after processing all the fragments.
|
||||
for _, fragment := range fragments {
|
||||
if ctx.OtherModuleHasProvider(fragment, HiddenAPIInfoProvider) {
|
||||
info := ctx.OtherModuleProvider(fragment, HiddenAPIInfoProvider).(HiddenAPIInfo)
|
||||
monolithicInfo.append(&info)
|
||||
}
|
||||
}
|
||||
|
||||
// Dedup paths.
|
||||
monolithicInfo.dedup()
|
||||
|
||||
return monolithicInfo
|
||||
}
|
||||
|
||||
// append appends all the files from the supplied info to the corresponding files in this struct.
|
||||
func (i *MonolithicHiddenAPIInfo) append(other *HiddenAPIInfo) {
|
||||
i.FlagsFilesByCategory.append(other.FlagFilesByCategory)
|
||||
i.StubFlagsPaths = append(i.StubFlagsPaths, other.StubFlagsPath)
|
||||
i.AnnotationFlagsPaths = append(i.AnnotationFlagsPaths, other.AnnotationFlagsPath)
|
||||
i.MetadataPaths = append(i.MetadataPaths, other.MetadataPath)
|
||||
i.IndexPaths = append(i.IndexPaths, other.IndexPath)
|
||||
i.AllFlagsPaths = append(i.AllFlagsPaths, other.AllFlagsPath)
|
||||
}
|
||||
|
||||
// dedup removes duplicates in all the paths, while maintaining the order in which they were
|
||||
// appended.
|
||||
func (i *MonolithicHiddenAPIInfo) dedup() {
|
||||
i.FlagsFilesByCategory.dedup()
|
||||
i.StubFlagsPaths = android.FirstUniquePaths(i.StubFlagsPaths)
|
||||
i.AnnotationFlagsPaths = android.FirstUniquePaths(i.AnnotationFlagsPaths)
|
||||
i.MetadataPaths = android.FirstUniquePaths(i.MetadataPaths)
|
||||
i.IndexPaths = android.FirstUniquePaths(i.IndexPaths)
|
||||
i.AllFlagsPaths = android.FirstUniquePaths(i.AllFlagsPaths)
|
||||
}
|
||||
|
||||
var monolithicHiddenAPIInfoProvider = blueprint.NewProvider(MonolithicHiddenAPIInfo{})
|
|
@ -279,25 +279,24 @@ func (b *platformBootclasspathModule) generateHiddenAPIBuildActions(ctx android.
|
|||
return
|
||||
}
|
||||
|
||||
flagFileInfo := b.properties.Hidden_api.hiddenAPIFlagFileInfo(ctx)
|
||||
for _, fragment := range fragments {
|
||||
if ctx.OtherModuleHasProvider(fragment, hiddenAPIFlagFileInfoProvider) {
|
||||
info := ctx.OtherModuleProvider(fragment, hiddenAPIFlagFileInfoProvider).(hiddenAPIFlagFileInfo)
|
||||
flagFileInfo.append(info)
|
||||
}
|
||||
}
|
||||
monolithicInfo := b.createAndProvideMonolithicHiddenAPIInfo(ctx, fragments)
|
||||
|
||||
// Store the information for testing.
|
||||
ctx.SetProvider(hiddenAPIFlagFileInfoProvider, flagFileInfo)
|
||||
// Create the input to pass to ruleToGenerateHiddenAPIStubFlagsFile
|
||||
input := newHiddenAPIFlagInput()
|
||||
|
||||
// Gather stub library information from the dependencies on modules provided by
|
||||
// hiddenAPIComputeMonolithicStubLibModules.
|
||||
input.gatherStubLibInfo(ctx, nil)
|
||||
|
||||
// Use the flag files from this module and all the fragments.
|
||||
input.FlagFilesByCategory = monolithicInfo.FlagsFilesByCategory
|
||||
|
||||
hiddenAPIModules := gatherHiddenAPIModuleFromContents(ctx, modules)
|
||||
|
||||
sdkKindToStubPaths := hiddenAPIGatherStubLibDexJarPaths(ctx, nil)
|
||||
|
||||
// Generate the monolithic stub-flags.csv file.
|
||||
bootDexJars := extractBootDexJarsFromHiddenAPIModules(ctx, hiddenAPIModules)
|
||||
stubFlags := hiddenAPISingletonPaths(ctx).stubFlags
|
||||
rule := ruleToGenerateHiddenAPIStubFlagsFile(ctx, stubFlags, bootDexJars, sdkKindToStubPaths)
|
||||
rule := ruleToGenerateHiddenAPIStubFlagsFile(ctx, stubFlags, bootDexJars, input)
|
||||
rule.Build("platform-bootclasspath-monolithic-hiddenapi-stub-flags", "monolithic hidden API stub flags")
|
||||
|
||||
// Extract the classes jars from the contents.
|
||||
|
@ -309,7 +308,7 @@ func (b *platformBootclasspathModule) generateHiddenAPIBuildActions(ctx android.
|
|||
|
||||
// Generate the monotlithic hiddenapi-flags.csv file.
|
||||
allFlags := hiddenAPISingletonPaths(ctx).flags
|
||||
buildRuleToGenerateHiddenApiFlags(ctx, "hiddenAPIFlagsFile", "hiddenapi flags", allFlags, stubFlags, annotationFlags, &flagFileInfo)
|
||||
buildRuleToGenerateHiddenApiFlags(ctx, "hiddenAPIFlagsFile", "hiddenapi flags", allFlags, stubFlags, annotationFlags, monolithicInfo.FlagsFilesByCategory, monolithicInfo.AllFlagsPaths)
|
||||
|
||||
// Generate an intermediate monolithic hiddenapi-metadata.csv file directly from the annotations
|
||||
// in the source code.
|
||||
|
@ -328,6 +327,25 @@ func (b *platformBootclasspathModule) generateHiddenAPIBuildActions(ctx android.
|
|||
buildRuleToGenerateIndex(ctx, "monolithic hidden API index", classesJars, indexCSV)
|
||||
}
|
||||
|
||||
// createAndProvideMonolithicHiddenAPIInfo creates a MonolithicHiddenAPIInfo and provides it for
|
||||
// testing.
|
||||
func (b *platformBootclasspathModule) createAndProvideMonolithicHiddenAPIInfo(ctx android.ModuleContext, fragments []android.Module) MonolithicHiddenAPIInfo {
|
||||
// Create a temporary input structure in which to collate information provided directly by this
|
||||
// module, either through properties or direct dependencies.
|
||||
temporaryInput := newHiddenAPIFlagInput()
|
||||
|
||||
// Create paths to the flag files specified in the properties.
|
||||
temporaryInput.extractFlagFilesFromProperties(ctx, &b.properties.Hidden_api)
|
||||
|
||||
// Create the monolithic info, by starting with the flag files specified on this and then merging
|
||||
// in information from all the fragment dependencies of this.
|
||||
monolithicInfo := newMonolithicHiddenAPIInfo(ctx, temporaryInput.FlagFilesByCategory, fragments)
|
||||
|
||||
// Store the information for testing.
|
||||
ctx.SetProvider(monolithicHiddenAPIInfoProvider, monolithicInfo)
|
||||
return monolithicInfo
|
||||
}
|
||||
|
||||
func (b *platformBootclasspathModule) buildRuleMergeCSV(ctx android.ModuleContext, desc string, inputPaths android.Paths, outputPath android.WritablePath) {
|
||||
rule := android.NewRuleBuilder(pctx, ctx)
|
||||
rule.Command().
|
||||
|
|
|
@ -245,14 +245,14 @@ func TestPlatformBootclasspath_Fragments(t *testing.T) {
|
|||
).RunTest(t)
|
||||
|
||||
pbcp := result.Module("platform-bootclasspath", "android_common")
|
||||
info := result.ModuleProvider(pbcp, hiddenAPIFlagFileInfoProvider).(hiddenAPIFlagFileInfo)
|
||||
info := result.ModuleProvider(pbcp, monolithicHiddenAPIInfoProvider).(MonolithicHiddenAPIInfo)
|
||||
|
||||
for _, category := range hiddenAPIFlagFileCategories {
|
||||
name := category.propertyName
|
||||
message := fmt.Sprintf("category %s", name)
|
||||
filename := strings.ReplaceAll(name, "_", "-")
|
||||
expected := []string{fmt.Sprintf("%s.txt", filename), fmt.Sprintf("bar-%s.txt", filename)}
|
||||
android.AssertPathsRelativeToTopEquals(t, message, expected, info.categoryToPaths[category])
|
||||
android.AssertPathsRelativeToTopEquals(t, message, expected, info.FlagsFilesByCategory[category])
|
||||
}
|
||||
|
||||
android.AssertPathsRelativeToTopEquals(t, "stub flags", []string{"out/soong/.intermediates/bar-fragment/android_common/modular-hiddenapi/stub-flags.csv"}, info.StubFlagsPaths)
|
||||
|
|
Loading…
Reference in New Issue