| // Copyright 2019 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 sdk |
| |
| import ( |
| "log" |
| "os" |
| "runtime" |
| "testing" |
| |
| "android/soong/android" |
| "android/soong/java" |
| |
| "github.com/google/blueprint/proptools" |
| ) |
| |
| // Needed in an _test.go file in this package to ensure tests run correctly, particularly in IDE. |
| func TestMain(m *testing.M) { |
| if runtime.GOOS != "linux" { |
| // b/145598135 - Generating host snapshots for anything other than linux is not supported. |
| log.Printf("Skipping as sdk snapshot generation is only supported on linux not %s", runtime.GOOS) |
| os.Exit(0) |
| } |
| |
| os.Exit(m.Run()) |
| } |
| |
| // Ensure that prebuilt modules have the same effective visibility as the source |
| // modules. |
| func TestSnapshotVisibility(t *testing.T) { |
| packageBp := ` |
| package { |
| default_visibility: ["//other/foo"], |
| } |
| |
| sdk { |
| name: "mysdk", |
| visibility: [ |
| "//other/foo", |
| // This short form will be replaced with //package:__subpackages__ in the |
| // generated sdk_snapshot. |
| ":__subpackages__", |
| ], |
| prebuilt_visibility: [ |
| "//prebuilts/mysdk", |
| ], |
| java_header_libs: [ |
| "myjavalib", |
| "mypublicjavalib", |
| "mydefaultedjavalib", |
| "myprivatejavalib", |
| ], |
| } |
| |
| java_library { |
| name: "myjavalib", |
| // Uses package default visibility |
| srcs: ["Test.java"], |
| system_modules: "none", |
| sdk_version: "none", |
| } |
| |
| java_defaults { |
| name: "java-defaults", |
| visibility: ["//other/bar"], |
| } |
| |
| java_library { |
| name: "mypublicjavalib", |
| defaults: ["java-defaults"], |
| visibility: ["//visibility:public"], |
| srcs: ["Test.java"], |
| system_modules: "none", |
| sdk_version: "none", |
| } |
| |
| java_defaults { |
| name: "myjavadefaults", |
| visibility: ["//other/bar"], |
| } |
| |
| java_library { |
| name: "mydefaultedjavalib", |
| defaults: ["myjavadefaults"], |
| srcs: ["Test.java"], |
| system_modules: "none", |
| sdk_version: "none", |
| } |
| |
| java_library { |
| name: "myprivatejavalib", |
| srcs: ["Test.java"], |
| visibility: ["//visibility:private"], |
| system_modules: "none", |
| sdk_version: "none", |
| } |
| ` |
| |
| result := testSdkWithFs(t, ``, |
| map[string][]byte{ |
| "package/Test.java": nil, |
| "package/Android.bp": []byte(packageBp), |
| }) |
| |
| CheckSnapshot(t, result, "mysdk", "package", |
| checkAndroidBpContents(` |
| // This is auto-generated. DO NOT EDIT. |
| |
| java_import { |
| name: "mysdk_myjavalib@current", |
| sdk_member_name: "myjavalib", |
| visibility: [ |
| "//other/foo", |
| "//package", |
| "//prebuilts/mysdk", |
| ], |
| apex_available: ["//apex_available:platform"], |
| jars: ["java/myjavalib.jar"], |
| } |
| |
| java_import { |
| name: "myjavalib", |
| prefer: false, |
| visibility: [ |
| "//other/foo", |
| "//package", |
| "//prebuilts/mysdk", |
| ], |
| apex_available: ["//apex_available:platform"], |
| jars: ["java/myjavalib.jar"], |
| } |
| |
| java_import { |
| name: "mysdk_mypublicjavalib@current", |
| sdk_member_name: "mypublicjavalib", |
| visibility: ["//visibility:public"], |
| apex_available: ["//apex_available:platform"], |
| jars: ["java/mypublicjavalib.jar"], |
| } |
| |
| java_import { |
| name: "mypublicjavalib", |
| prefer: false, |
| visibility: ["//visibility:public"], |
| apex_available: ["//apex_available:platform"], |
| jars: ["java/mypublicjavalib.jar"], |
| } |
| |
| java_import { |
| name: "mysdk_mydefaultedjavalib@current", |
| sdk_member_name: "mydefaultedjavalib", |
| visibility: [ |
| "//other/bar", |
| "//package", |
| "//prebuilts/mysdk", |
| ], |
| apex_available: ["//apex_available:platform"], |
| jars: ["java/mydefaultedjavalib.jar"], |
| } |
| |
| java_import { |
| name: "mydefaultedjavalib", |
| prefer: false, |
| visibility: [ |
| "//other/bar", |
| "//package", |
| "//prebuilts/mysdk", |
| ], |
| apex_available: ["//apex_available:platform"], |
| jars: ["java/mydefaultedjavalib.jar"], |
| } |
| |
| java_import { |
| name: "mysdk_myprivatejavalib@current", |
| sdk_member_name: "myprivatejavalib", |
| visibility: [ |
| "//package", |
| "//prebuilts/mysdk", |
| ], |
| apex_available: ["//apex_available:platform"], |
| jars: ["java/myprivatejavalib.jar"], |
| } |
| |
| java_import { |
| name: "myprivatejavalib", |
| prefer: false, |
| visibility: [ |
| "//package", |
| "//prebuilts/mysdk", |
| ], |
| apex_available: ["//apex_available:platform"], |
| jars: ["java/myprivatejavalib.jar"], |
| } |
| |
| sdk_snapshot { |
| name: "mysdk@current", |
| visibility: [ |
| "//other/foo", |
| "//package:__subpackages__", |
| ], |
| java_header_libs: [ |
| "mysdk_myjavalib@current", |
| "mysdk_mypublicjavalib@current", |
| "mysdk_mydefaultedjavalib@current", |
| "mysdk_myprivatejavalib@current", |
| ], |
| } |
| `)) |
| } |
| |
| func TestPrebuiltVisibilityProperty_IsValidated(t *testing.T) { |
| testSdkError(t, `prebuilt_visibility: cannot mix "//visibility:private" with any other visibility rules`, ` |
| sdk { |
| name: "mysdk", |
| prebuilt_visibility: [ |
| "//foo", |
| "//visibility:private", |
| ], |
| } |
| `) |
| } |
| |
| func TestPrebuiltVisibilityProperty_AddPrivate(t *testing.T) { |
| testSdkError(t, `prebuilt_visibility: "//visibility:private" does not widen the visibility`, ` |
| sdk { |
| name: "mysdk", |
| prebuilt_visibility: [ |
| "//visibility:private", |
| ], |
| java_header_libs: [ |
| "myjavalib", |
| ], |
| } |
| |
| java_library { |
| name: "myjavalib", |
| // Uses package default visibility |
| srcs: ["Test.java"], |
| system_modules: "none", |
| sdk_version: "none", |
| } |
| `) |
| } |
| |
| func TestSdkInstall(t *testing.T) { |
| sdk := ` |
| sdk { |
| name: "mysdk", |
| } |
| ` |
| result := testSdkWithFs(t, sdk, nil) |
| |
| CheckSnapshot(t, result, "mysdk", "", |
| checkAllOtherCopyRules(`.intermediates/mysdk/common_os/mysdk-current.zip -> mysdk-current.zip`)) |
| } |
| |
| type EmbeddedPropertiesStruct struct { |
| S_Embedded_Common string `android:"arch_variant"` |
| S_Embedded_Different string `android:"arch_variant"` |
| } |
| |
| type testPropertiesStruct struct { |
| name string |
| private string |
| Public_Kept string `sdk:"keep"` |
| S_Common string |
| S_Different string `android:"arch_variant"` |
| A_Common []string |
| A_Different []string `android:"arch_variant"` |
| F_Common *bool |
| F_Different *bool `android:"arch_variant"` |
| EmbeddedPropertiesStruct |
| } |
| |
| func (p *testPropertiesStruct) optimizableProperties() interface{} { |
| return p |
| } |
| |
| func (p *testPropertiesStruct) String() string { |
| return p.name |
| } |
| |
| var _ propertiesContainer = (*testPropertiesStruct)(nil) |
| |
| func TestCommonValueOptimization(t *testing.T) { |
| common := &testPropertiesStruct{name: "common"} |
| structs := []propertiesContainer{ |
| &testPropertiesStruct{ |
| name: "struct-0", |
| private: "common", |
| Public_Kept: "common", |
| S_Common: "common", |
| S_Different: "upper", |
| A_Common: []string{"first", "second"}, |
| A_Different: []string{"alpha", "beta"}, |
| F_Common: proptools.BoolPtr(false), |
| F_Different: proptools.BoolPtr(false), |
| EmbeddedPropertiesStruct: EmbeddedPropertiesStruct{ |
| S_Embedded_Common: "embedded_common", |
| S_Embedded_Different: "embedded_upper", |
| }, |
| }, |
| &testPropertiesStruct{ |
| name: "struct-1", |
| private: "common", |
| Public_Kept: "common", |
| S_Common: "common", |
| S_Different: "lower", |
| A_Common: []string{"first", "second"}, |
| A_Different: []string{"alpha", "delta"}, |
| F_Common: proptools.BoolPtr(false), |
| F_Different: proptools.BoolPtr(true), |
| EmbeddedPropertiesStruct: EmbeddedPropertiesStruct{ |
| S_Embedded_Common: "embedded_common", |
| S_Embedded_Different: "embedded_lower", |
| }, |
| }, |
| } |
| |
| extractor := newCommonValueExtractor(common) |
| |
| err := extractor.extractCommonProperties(common, structs) |
| android.AssertDeepEquals(t, "unexpected error", nil, err) |
| |
| android.AssertDeepEquals(t, "common properties not correct", |
| &testPropertiesStruct{ |
| name: "common", |
| private: "", |
| Public_Kept: "", |
| S_Common: "common", |
| S_Different: "", |
| A_Common: []string{"first", "second"}, |
| A_Different: []string(nil), |
| F_Common: proptools.BoolPtr(false), |
| F_Different: nil, |
| EmbeddedPropertiesStruct: EmbeddedPropertiesStruct{ |
| S_Embedded_Common: "embedded_common", |
| S_Embedded_Different: "", |
| }, |
| }, |
| common) |
| |
| android.AssertDeepEquals(t, "updated properties[0] not correct", |
| &testPropertiesStruct{ |
| name: "struct-0", |
| private: "common", |
| Public_Kept: "common", |
| S_Common: "", |
| S_Different: "upper", |
| A_Common: nil, |
| A_Different: []string{"alpha", "beta"}, |
| F_Common: nil, |
| F_Different: proptools.BoolPtr(false), |
| EmbeddedPropertiesStruct: EmbeddedPropertiesStruct{ |
| S_Embedded_Common: "", |
| S_Embedded_Different: "embedded_upper", |
| }, |
| }, |
| structs[0]) |
| |
| android.AssertDeepEquals(t, "updated properties[1] not correct", |
| &testPropertiesStruct{ |
| name: "struct-1", |
| private: "common", |
| Public_Kept: "common", |
| S_Common: "", |
| S_Different: "lower", |
| A_Common: nil, |
| A_Different: []string{"alpha", "delta"}, |
| F_Common: nil, |
| F_Different: proptools.BoolPtr(true), |
| EmbeddedPropertiesStruct: EmbeddedPropertiesStruct{ |
| S_Embedded_Common: "", |
| S_Embedded_Different: "embedded_lower", |
| }, |
| }, |
| structs[1]) |
| } |
| |
| func TestCommonValueOptimization_InvalidArchSpecificVariants(t *testing.T) { |
| common := &testPropertiesStruct{name: "common"} |
| structs := []propertiesContainer{ |
| &testPropertiesStruct{ |
| name: "struct-0", |
| S_Common: "should-be-but-is-not-common0", |
| }, |
| &testPropertiesStruct{ |
| name: "struct-1", |
| S_Common: "should-be-but-is-not-common1", |
| }, |
| } |
| |
| extractor := newCommonValueExtractor(common) |
| |
| err := extractor.extractCommonProperties(common, structs) |
| android.AssertErrorMessageEquals(t, "unexpected error", `field "S_Common" is not tagged as "arch_variant" but has arch specific properties: |
| "struct-0" has value "should-be-but-is-not-common0" |
| "struct-1" has value "should-be-but-is-not-common1"`, err) |
| } |
| |
| // Ensure that sdk snapshot related environment variables work correctly. |
| func TestSnapshot_EnvConfiguration(t *testing.T) { |
| bp := ` |
| sdk { |
| name: "mysdk", |
| java_header_libs: ["myjavalib"], |
| } |
| |
| java_library { |
| name: "myjavalib", |
| srcs: ["Test.java"], |
| system_modules: "none", |
| sdk_version: "none", |
| compile_dex: true, |
| host_supported: true, |
| } |
| ` |
| preparer := android.GroupFixturePreparers( |
| prepareForSdkTestWithJava, |
| android.FixtureWithRootAndroidBp(bp), |
| ) |
| |
| checkZipFile := func(t *testing.T, result *android.TestResult, expected string) { |
| zipRule := result.ModuleForTests("mysdk", "common_os").Rule("SnapshotZipFiles") |
| android.AssertStringEquals(t, "snapshot zip file", expected, zipRule.Output.String()) |
| } |
| |
| t.Run("no env variables", func(t *testing.T) { |
| result := preparer.RunTest(t) |
| |
| checkZipFile(t, result, "out/soong/.intermediates/mysdk/common_os/mysdk-current.zip") |
| |
| CheckSnapshot(t, result, "mysdk", "", |
| checkAndroidBpContents(` |
| // This is auto-generated. DO NOT EDIT. |
| |
| java_import { |
| name: "mysdk_myjavalib@current", |
| sdk_member_name: "myjavalib", |
| visibility: ["//visibility:public"], |
| apex_available: ["//apex_available:platform"], |
| jars: ["java/myjavalib.jar"], |
| } |
| |
| java_import { |
| name: "myjavalib", |
| prefer: false, |
| visibility: ["//visibility:public"], |
| apex_available: ["//apex_available:platform"], |
| jars: ["java/myjavalib.jar"], |
| } |
| |
| sdk_snapshot { |
| name: "mysdk@current", |
| visibility: ["//visibility:public"], |
| java_header_libs: ["mysdk_myjavalib@current"], |
| } |
| `), |
| ) |
| }) |
| |
| t.Run("SOONG_SDK_SNAPSHOT_PREFER=true", func(t *testing.T) { |
| result := android.GroupFixturePreparers( |
| preparer, |
| android.FixtureMergeEnv(map[string]string{ |
| "SOONG_SDK_SNAPSHOT_PREFER": "true", |
| }), |
| ).RunTest(t) |
| |
| checkZipFile(t, result, "out/soong/.intermediates/mysdk/common_os/mysdk-current.zip") |
| |
| CheckSnapshot(t, result, "mysdk", "", |
| checkAndroidBpContents(` |
| // This is auto-generated. DO NOT EDIT. |
| |
| java_import { |
| name: "mysdk_myjavalib@current", |
| sdk_member_name: "myjavalib", |
| visibility: ["//visibility:public"], |
| apex_available: ["//apex_available:platform"], |
| jars: ["java/myjavalib.jar"], |
| } |
| |
| java_import { |
| name: "myjavalib", |
| prefer: true, |
| visibility: ["//visibility:public"], |
| apex_available: ["//apex_available:platform"], |
| jars: ["java/myjavalib.jar"], |
| } |
| |
| sdk_snapshot { |
| name: "mysdk@current", |
| visibility: ["//visibility:public"], |
| java_header_libs: ["mysdk_myjavalib@current"], |
| } |
| `), |
| ) |
| }) |
| |
| t.Run("SOONG_SDK_SNAPSHOT_USE_SOURCE_CONFIG_VAR=module:build_from_source", func(t *testing.T) { |
| result := android.GroupFixturePreparers( |
| preparer, |
| android.FixtureMergeEnv(map[string]string{ |
| "SOONG_SDK_SNAPSHOT_USE_SOURCE_CONFIG_VAR": "module:build_from_source", |
| }), |
| ).RunTest(t) |
| |
| checkZipFile(t, result, "out/soong/.intermediates/mysdk/common_os/mysdk-current.zip") |
| |
| CheckSnapshot(t, result, "mysdk", "", |
| checkAndroidBpContents(` |
| // This is auto-generated. DO NOT EDIT. |
| |
| java_import { |
| name: "mysdk_myjavalib@current", |
| sdk_member_name: "myjavalib", |
| visibility: ["//visibility:public"], |
| apex_available: ["//apex_available:platform"], |
| jars: ["java/myjavalib.jar"], |
| } |
| |
| java_import { |
| name: "myjavalib", |
| prefer: false, |
| use_source_config_var: { |
| config_namespace: "module", |
| var_name: "build_from_source", |
| }, |
| visibility: ["//visibility:public"], |
| apex_available: ["//apex_available:platform"], |
| jars: ["java/myjavalib.jar"], |
| } |
| |
| sdk_snapshot { |
| name: "mysdk@current", |
| visibility: ["//visibility:public"], |
| java_header_libs: ["mysdk_myjavalib@current"], |
| } |
| `), |
| ) |
| }) |
| |
| t.Run("SOONG_SDK_SNAPSHOT_VERSION=unversioned", func(t *testing.T) { |
| result := android.GroupFixturePreparers( |
| preparer, |
| android.FixtureMergeEnv(map[string]string{ |
| "SOONG_SDK_SNAPSHOT_VERSION": "unversioned", |
| }), |
| ).RunTest(t) |
| |
| checkZipFile(t, result, "out/soong/.intermediates/mysdk/common_os/mysdk.zip") |
| |
| CheckSnapshot(t, result, "mysdk", "", |
| checkAndroidBpContents(` |
| // This is auto-generated. DO NOT EDIT. |
| |
| java_import { |
| name: "myjavalib", |
| prefer: false, |
| visibility: ["//visibility:public"], |
| apex_available: ["//apex_available:platform"], |
| jars: ["java/myjavalib.jar"], |
| } |
| `), |
| ) |
| }) |
| |
| t.Run("SOONG_SDK_SNAPSHOT_VERSION=current", func(t *testing.T) { |
| result := android.GroupFixturePreparers( |
| preparer, |
| android.FixtureMergeEnv(map[string]string{ |
| "SOONG_SDK_SNAPSHOT_VERSION": "current", |
| }), |
| ).RunTest(t) |
| |
| checkZipFile(t, result, "out/soong/.intermediates/mysdk/common_os/mysdk-current.zip") |
| |
| CheckSnapshot(t, result, "mysdk", "", |
| checkAndroidBpContents(` |
| // This is auto-generated. DO NOT EDIT. |
| |
| java_import { |
| name: "mysdk_myjavalib@current", |
| sdk_member_name: "myjavalib", |
| visibility: ["//visibility:public"], |
| apex_available: ["//apex_available:platform"], |
| jars: ["java/myjavalib.jar"], |
| } |
| |
| java_import { |
| name: "myjavalib", |
| prefer: false, |
| visibility: ["//visibility:public"], |
| apex_available: ["//apex_available:platform"], |
| jars: ["java/myjavalib.jar"], |
| } |
| |
| sdk_snapshot { |
| name: "mysdk@current", |
| visibility: ["//visibility:public"], |
| java_header_libs: ["mysdk_myjavalib@current"], |
| } |
| `), |
| ) |
| }) |
| |
| t.Run("SOONG_SDK_SNAPSHOT_VERSION=2", func(t *testing.T) { |
| result := android.GroupFixturePreparers( |
| preparer, |
| android.FixtureMergeEnv(map[string]string{ |
| "SOONG_SDK_SNAPSHOT_VERSION": "2", |
| }), |
| ).RunTest(t) |
| |
| checkZipFile(t, result, "out/soong/.intermediates/mysdk/common_os/mysdk-2.zip") |
| |
| CheckSnapshot(t, result, "mysdk", "", |
| checkAndroidBpContents(` |
| // This is auto-generated. DO NOT EDIT. |
| |
| java_import { |
| name: "mysdk_myjavalib@2", |
| sdk_member_name: "myjavalib", |
| visibility: ["//visibility:public"], |
| apex_available: ["//apex_available:platform"], |
| jars: ["java/myjavalib.jar"], |
| } |
| |
| sdk_snapshot { |
| name: "mysdk@2", |
| visibility: ["//visibility:public"], |
| java_header_libs: ["mysdk_myjavalib@2"], |
| } |
| `), |
| // A versioned snapshot cannot be used on its own so add the source back in. |
| snapshotTestPreparer(checkSnapshotWithoutSource, android.FixtureWithRootAndroidBp(bp)), |
| ) |
| }) |
| |
| t.Run("SOONG_SDK_SNAPSHOT_TARGET_BUILD_RELEASE=S", func(t *testing.T) { |
| result := android.GroupFixturePreparers( |
| prepareForSdkTestWithJava, |
| java.PrepareForTestWithJavaDefaultModules, |
| java.PrepareForTestWithJavaSdkLibraryFiles, |
| java.FixtureWithLastReleaseApis("mysdklibrary"), |
| android.FixtureWithRootAndroidBp(` |
| sdk { |
| name: "mysdk", |
| bootclasspath_fragments: ["mybootclasspathfragment"], |
| } |
| |
| bootclasspath_fragment { |
| name: "mybootclasspathfragment", |
| apex_available: ["myapex"], |
| contents: ["mysdklibrary"], |
| } |
| |
| java_sdk_library { |
| name: "mysdklibrary", |
| srcs: ["Test.java"], |
| compile_dex: true, |
| public: {enabled: true}, |
| permitted_packages: ["mysdklibrary"], |
| } |
| `), |
| android.FixtureMergeEnv(map[string]string{ |
| "SOONG_SDK_SNAPSHOT_TARGET_BUILD_RELEASE": "S", |
| }), |
| ).RunTest(t) |
| |
| CheckSnapshot(t, result, "mysdk", "", |
| checkUnversionedAndroidBpContents(` |
| // This is auto-generated. DO NOT EDIT. |
| |
| prebuilt_bootclasspath_fragment { |
| name: "mybootclasspathfragment", |
| prefer: false, |
| visibility: ["//visibility:public"], |
| apex_available: ["myapex"], |
| contents: ["mysdklibrary"], |
| hidden_api: { |
| annotation_flags: "hiddenapi/annotation-flags.csv", |
| metadata: "hiddenapi/metadata.csv", |
| index: "hiddenapi/index.csv", |
| stub_flags: "hiddenapi/stub-flags.csv", |
| all_flags: "hiddenapi/all-flags.csv", |
| }, |
| } |
| |
| java_sdk_library_import { |
| name: "mysdklibrary", |
| prefer: false, |
| visibility: ["//visibility:public"], |
| apex_available: ["//apex_available:platform"], |
| shared_library: true, |
| compile_dex: true, |
| permitted_packages: ["mysdklibrary"], |
| public: { |
| jars: ["sdk_library/public/mysdklibrary-stubs.jar"], |
| stub_srcs: ["sdk_library/public/mysdklibrary_stub_sources"], |
| current_api: "sdk_library/public/mysdklibrary.txt", |
| removed_api: "sdk_library/public/mysdklibrary-removed.txt", |
| sdk_version: "current", |
| }, |
| } |
| `), |
| |
| checkAllCopyRules(` |
| .intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/annotation-flags.csv -> hiddenapi/annotation-flags.csv |
| .intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/metadata.csv -> hiddenapi/metadata.csv |
| .intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/index.csv -> hiddenapi/index.csv |
| .intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/stub-flags.csv -> hiddenapi/stub-flags.csv |
| .intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/all-flags.csv -> hiddenapi/all-flags.csv |
| .intermediates/mysdklibrary.stubs/android_common/javac/mysdklibrary.stubs.jar -> sdk_library/public/mysdklibrary-stubs.jar |
| .intermediates/mysdklibrary.stubs.source/android_common/metalava/mysdklibrary.stubs.source_api.txt -> sdk_library/public/mysdklibrary.txt |
| .intermediates/mysdklibrary.stubs.source/android_common/metalava/mysdklibrary.stubs.source_removed.txt -> sdk_library/public/mysdklibrary-removed.txt |
| `), |
| ) |
| }) |
| |
| } |