blob: b04c9eb904141f8de15fd990f43c0bb2d327cdb8 [file]
#!/bin/bash -eu
set -o pipefail
function extend_mock_top_for_java_or_kotlin_partial_compile_test {
# Required for building java_library type modules.
symlink_directory prebuilts/jdk
symlink_directory prebuilts/gcc
symlink_directory external/turbine
symlink_directory external/gson
symlink_directory external/ow2-asm
symlink_directory external/auto
symlink_directory external/icu
symlink_directory external/zlib
symlink_directory external/guava
symlink_directory external/jspecify
symlink_directory external/error_prone
symlink_directory external/jsr305
symlink_directory external/javapoet
symlink_directory external/escapevelocity
symlink_directory external/kotlinx.metadata
symlink_directory external/kotlinc
symlink_directory system/logging/liblog
symlink_directory external/abseil-cpp
mkdir -p "$MOCK_TOP"/tools/lint_checks
cat > "$MOCK_TOP"/tools/lint_checks/Android.bp <<EOF
java_library_host {
name: "AndroidGlobalLintChecker",
}
EOF
write_soong_vars soong.test_arm64.variables
}
function extend_mock_top_for_java_partial_compile_test {
extend_mock_top_for_java_or_kotlin_partial_compile_test
# Pre-compile impl-library using partial compile.
partial_compile_setup ${impl_library_jar}
run_soong
run_ninja ${impl_library_jar}
}
function extend_mock_top_for_kotlin_partial_compile_test {
extend_mock_top_for_java_or_kotlin_partial_compile_test
symlink_directory prebuilts/tools/common/m2
symlink_directory external/kotlinx.coroutines
symlink_directory external/kotlinx.atomicfu
symlink_directory sdk/annotations
partial_compile_setup ${impl_library_jar}
create_kotlin_files
run_soong
run_ninja ${impl_library_jar}
}
# This file is generated by soong during make, here we have copy pasted the
# exact same file, with VendorApiLevel added as an extra.
# shellcheck disable=SC2120
function write_soong_vars {
local soong_out=$MOCK_TOP/out/soong
local variables_file_name="$1"
local tmp_variables_file="$soong_out"/"$variables_file_name".tmp
local variables_file="$soong_out"/"$variables_file_name"
mkdir -p "$soong_out"
cat > "$tmp_variables_file" << EOF
{
"BuildNumberFile": "build_number.txt",
"Platform_version_name": "S",
"Platform_sdk_version": 30,
"Platform_sdk_codename": "S",
"Platform_sdk_final": false,
"Platform_base_sdk_extension_version": 30,
"Platform_version_active_codenames": [
"S"
],
"Platform_version_all_preview_codenames": [
"S"
],
"DeviceName": "test_arm64",
"DeviceProduct": "test_arm64",
"DeviceArch": "arm64",
"DeviceArchVariant": "armv8-a",
"DeviceCpuVariant": "generic",
"DeviceAbi": [
"arm64-v8a"
],
"DeviceMaxPageSizeSupported": "4096",
"DeviceNoBionicPageSizeMacro": false,
"DeviceSecondaryArch": "arm",
"DeviceSecondaryArchVariant": "armv8-a",
"DeviceSecondaryCpuVariant": "generic",
"DeviceSecondaryAbi": [
"armeabi-v7a",
"armeabi"
],
"HostArch": "x86_64",
"HostSecondaryArch": "x86",
"CrossHost": "windows",
"CrossHostArch": "x86",
"CrossHostSecondaryArch": "x86_64",
"AAPTCharacteristics": "nosdcard",
"AAPTConfig": [
"normal",
"large",
"xlarge",
"hdpi",
"xhdpi",
"xxhdpi"
],
"AAPTPreferredConfig": "xhdpi",
"AAPTPrebuiltDPI": [
"xhdpi",
"xxhdpi"
],
"Malloc_low_memory": false,
"Malloc_zero_contents": true,
"Malloc_pattern_fill_contents": false,
"Safestack": false,
"Build_from_text_stub": false,
"BootJars": [],
"ApexBootJars": [],
"PartitionVarsForSoongMigrationOnlyDoNotUse": {
"PartitionQualifiedVariables": null
},
"CompatibilityTestcases": null,
"VendorApiLevel": "202604",
"Eng": true
}
EOF
if cmp -s "$tmp_variables_file" "$variables_file"; then
rm "$tmp_variables_file"
else
mv -f "$tmp_variables_file" "$variables_file"
fi
}
function create_base_files {
mkdir -p soong-test/java/integration/impllib
cat > soong-test/java/integration/Android.bp <<'EOF'
java_library {
name: "impl-library",
srcs: [
"impllib/**/*.java",
],
libs: ["provider-library"],
sdk_version: "35",
}
java_library {
name: "provider-library",
srcs: ["provider/**/*.java"],
sdk_version: "35",
}
EOF
cat > soong-test/java/integration/impllib/ExampleImpl1.java <<'EOF'
package soong.java.integration.impllib;
import soong.java.integration.provider.Provider;
class ExampleImpl1 {
public static final int EXAMPLE_1_CONST_1 = 100;
private Provider mProvider;
public ExampleImpl1() {
mProvider = new Provider();
}
public String getClassName() {
return "ExampleInt1";
}
public int getProviderConstant() {
return mProvider.nextProviderConst();
}
}
EOF
mkdir -p soong-test/java/integration/provider
cat > soong-test/java/integration/provider/Provider.java <<'EOF'
package soong.java.integration.provider;
public class Provider {
public static final int PROVIDER_CONST = -1;
private String className;
public Provider() {
this.className = "Provider";
}
public int nextProviderConst() {
return PROVIDER_CONST + 1;
}
private String getProviderName() {
return className;
}
}
EOF
}
function create_ap_files {
mkdir -p soong-test/java/integration/impllib
cat > soong-test/java/integration/Android.bp <<'EOF'
java_library {
name: "impl-library",
srcs: [
"impllib/**/*.java",
":wrapper-annotations-source",
],
libs: ["provider-library"],
plugins: ["wrapper-annotation-processor"],
sdk_version: "35",
}
java_library {
name: "provider-library",
srcs: ["provider/**/*.java"],
sdk_version: "35",
}
java_library_host {
name: "annotation-processor-lib",
srcs: ["annotation/**/*.java"],
}
java_plugin {
name: "wrapper-annotation-processor",
processor_class: "soong.java.integration.annotation.WrapperProcessor",
static_libs: ["annotation-processor-lib"],
installable: false,
generates_api: true,
}
filegroup {
name: "wrapper-annotations-source",
srcs: ["annotation/GenerateWrapper.java"],
}
EOF
mkdir -p soong-test/java/integration/annotation
cat > soong-test/java/integration/annotation/WrapperProcessor.java <<'EOF'
package soong.java.integration.annotation;
import soong.java.integration.annotation.GenerateWrapper;
import java.io.IOException;
import java.io.Writer;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.Elements;
import javax.tools.Diagnostic;
import javax.tools.JavaFileObject;
@SupportedAnnotationTypes("soong.java.integration.annotation.GenerateWrapper")
@SupportedSourceVersion(SourceVersion.RELEASE_17)
public class WrapperProcessor extends AbstractProcessor {
private Filer filer;
private Messager messager;
private Elements elementUtils;
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
filer = processingEnv.getFiler();
messager = processingEnv.getMessager();
elementUtils = processingEnv.getElementUtils();
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
for (Element annotatedElement : roundEnv.getElementsAnnotatedWith(GenerateWrapper.class)) {
ExecutableElement originalMethod = (ExecutableElement) annotatedElement;
GenerateWrapper annotation = originalMethod.getAnnotation(GenerateWrapper.class);
int methodCount = annotation.methodCount();
TypeElement enclosingClass = (TypeElement) originalMethod.getEnclosingElement();
String packageName = elementUtils.getPackageOf(enclosingClass).getQualifiedName().toString();
String originalClassNameForNaming = enclosingClass.getSimpleName().toString();
String wrapperClassName = originalClassNameForNaming + "Wrapper";
String wrapperMethodName = originalMethod.getSimpleName().toString() + "Wrapper";
try {
generateAlwaysVoidNoParamWrapper(packageName, wrapperClassName, wrapperMethodName, methodCount);
} catch (IOException e) {
throw new RuntimeException("Unable to generate class using annotation");
}
}
return true;
}
private void generateAlwaysVoidNoParamWrapper(String packageName, String wrapperClassName, String originalMethodNameForNaming, int methodCount) throws IOException {
StringBuilder sb = new StringBuilder();
sb.append("package ").append(packageName).append(";\n\n");
sb.append("public final class ").append(wrapperClassName).append(" {\n\n");
for (int i=0; i < methodCount; i++) {
sb.append(" public String ").append(originalMethodNameForNaming + String.valueOf(i)).append("() {\n");
sb.append(" return ").append("\"").append(wrapperClassName).append("\"").append(";\n");
sb.append(" }\n");
}
sb.append("}\n");
JavaFileObject jfo = filer.createSourceFile(packageName + "." + wrapperClassName);
try (Writer writer = jfo.openWriter()) {
writer.write(sb.toString());
}
}
}
EOF
cat > soong-test/java/integration/annotation/GenerateWrapper.java <<'EOF'
package soong.java.integration.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.METHOD)
public @interface GenerateWrapper {
int methodCount() default 1;
}
EOF
cat > soong-test/java/integration/impllib/AnnotationUsage.java <<'EOF'
package soong.java.integration.impllib;
import soong.java.integration.annotation.GenerateWrapper;
public class AnnotationUsage {
@GenerateWrapper(methodCount = 2)
public String getClassName() {
return "AnnotationUsage";
}
}
EOF
cat > soong-test/java/integration/impllib/ExampleImpl4.java <<'EOF'
package soong.java.integration.impllib;
import soong.java.integration.impllib.AnnotationUsageWrapper;
public class ExampleImpl4 {
private AnnotationUsageWrapper mAnnotationUsageWrapper;
public ExampleImpl4() {
this.mAnnotationUsageWrapper = new AnnotationUsageWrapper();
}
public String readGenApi() {
return mAnnotationUsageWrapper.getClassNameWrapper1();
}
}
EOF
}
create_example_impl3_file() {
mkdir -p soong-test/java/integration/impllib
cat > soong-test/java/integration/impllib/ExampleImpl3.java <<'EOF'
package soong.java.integration.impllib;
import soong.java.integration.impllib.ExampleImpl1;
public class ExampleImpl3 {
public ExampleImpl3() {
}
public String getClassName() {
return "ExampleImpl3";
}
private int getConstant() {
return ExampleImpl1.EXAMPLE_1_CONST_1;
}
}
EOF
}
modify_example_impl1_file() {
mkdir -p soong-test/java/integration/impllib
cat > soong-test/java/integration/impllib/ExampleImpl1.java <<'EOF'
package soong.java.integration.impllib;
import soong.java.integration.provider.Provider;
class ExampleImpl1 {
public static final int EXAMPLE_1_CONST_1_CHANGED = 10000;
private Provider mProvider;
public ExampleImpl1() {
mProvider = new Provider();
}
public String getClassName() {
return "ExampleInt1";
}
public int getProviderConstant() {
return mProvider.nextProviderConst();
}
}
EOF
}
modify_provider_file() {
mkdir -p soong-test/java/integration/provider
cat > soong-test/java/integration/provider/Provider.java <<'EOF'
package soong.java.integration.provider;
public class Provider {
public static final int PROVIDER_CONST = -1;
private String className;
public Provider() {
this.className = "Provider";
}
public int previousProviderConst() {
return PROVIDER_CONST - 1;
}
private String getProviderName() {
return className;
}
}
EOF
}
function modify_annotation_api {
mkdir -p soong-test/java/integration/impllib
cat > soong-test/java/integration/impllib/AnnotationUsage.java <<'EOF'
package soong.java.integration.impllib;
import soong.java.integration.annotation.GenerateWrapper;
public class AnnotationUsage {
@GenerateWrapper(methodCount = 1)
public String getClassName() {
return "AnnotationUsage";
}
}
EOF
}
function remove_base_dir {
rm -rf soong-test
}
# shellcheck disable=SC2120
function partial_compile_setup {
remove_base_dir
create_base_files
set_partial_compile_flags
local target_to_remove="${1:-}"
if [ -n "$target_to_remove" ]; then # Check if it's non-empty after default
echo "Removing target: $target_to_remove"
rm -rf "$target_to_remove"
fi
local all_files="${2:-}"
local all_files_create=false
if [[ "$all_files" == "true" ]]; then
all_files_create=true
fi
if [[ "$all_files_create" == true ]]; then
echo "Adding All Files:"
create_example_impl3_file
fi
}
function partial_compile_setup_with_ap {
remove_base_dir
create_base_files
create_ap_files
set_partial_compile_flags
}
# shellcheck disable=SC2120
function full_compile_setup {
remove_base_dir
create_base_files
unset_partial_compile_flags
local target_to_remove="${1:-}"
if [ -n "$target_to_remove" ]; then # Check if it's non-empty after default
echo "Removing target: $target_to_remove"
rm -rf "$target_to_remove"
fi
local all_files="${2:-}"
local all_files_create=false
if [[ "$all_files" == "true" ]]; then
all_files_create=true
fi
if [[ "$all_files_create" == true ]]; then
echo "Adding All Files:"
create_example_impl3_file
fi
}
function set_partial_compile_flags {
export SOONG_PARTIAL_COMPILE=true
export SOONG_USE_PARTIAL_COMPILE=true
}
function unset_partial_compile_flags {
export SOONG_PARTIAL_COMPILE=false
}
function create_kotlin_files {
cat > soong-test/java/integration/Android.bp <<'EOF'
java_library {
name: "impl-library",
srcs: [
"impllib/**/*.java",
"impllib/**/*.kt",
],
libs: ["provider-library"],
sdk_version: "35",
}
java_library {
name: "provider-library",
srcs: ["provider/**/*.java"],
sdk_version: "35",
}
EOF
cat > soong-test/java/integration/impllib/UsesKt.java <<'EOF'
package soong.java.integration.impllib;
class UsesKt {
private KtClass mKtClass;
public UsesKt() {
mKtClass = new KtClass();
}
public void CallKt() {
mKtClass.foo();
}
}
EOF
cat > soong-test/java/integration/impllib/KtClass.kt <<'EOF'
package soong.java.integration.impllib;
class KtClass() {
fun foo() { System.out.println("foo") }
}
EOF
}