Merge "Use same repository url in all configs"
diff --git a/infra/config/global/cr-buildbucket.cfg b/infra/config/global/cr-buildbucket.cfg
index 95c4545..6e9531a 100644
--- a/infra/config/global/cr-buildbucket.cfg
+++ b/infra/config/global/cr-buildbucket.cfg
@@ -95,9 +95,11 @@
}
builders {
name: "d8-linux"
- properties: "tool:d8"
- properties: "aosp_jar:True"
mixins: "linux"
+ recipe {
+ properties: "tool:d8"
+ properties: "aosp_jar:True"
+ }
}
builders {
name: "d8-linux-android-4.0.4"
@@ -133,9 +135,11 @@
}
builders {
name: "d8-linux-android-7.0.0"
- properties: "tool:d8"
- properties: "dex_vm:7.0.0"
mixins: "linux"
+ recipe {
+ properties: "tool:d8"
+ properties: "dex_vm:7.0.0"
+ }
}
builders {
name: "d8-linux-android-7.0.0_release"
@@ -156,7 +160,9 @@
builders {
name: "linux"
mixins: "linux"
- properties: "tool:r8"
+ recipe {
+ properties: "tool:r8"
+ }
}
builders {
name: "linux-android-4.0.4"
@@ -192,9 +198,11 @@
}
builders {
name: "linux-android-7.0.0"
- properties: "tool:r8"
- properties: "dex_vm:7.0.0"
mixins: "linux"
+ recipe {
+ properties: "tool:r8"
+ properties: "dex_vm:7.0.0"
+ }
}
builders {
name: "linux-android-7.0.0_release"
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardAccessFlags.java b/src/main/java/com/android/tools/r8/shaking/ProguardAccessFlags.java
index 9dfddb2..8522f1f 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardAccessFlags.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardAccessFlags.java
@@ -26,7 +26,9 @@
"transient",
"synchronized",
"native",
- "strictfp"
+ "strictfp",
+ "synthetic",
+ "bridge"
);
// Get ordered list of flag predicates. Must be consistent with getNames.
@@ -42,7 +44,9 @@
this::isTransient,
this::isSynchronized,
this::isNative,
- this::isStrict);
+ this::isStrict,
+ this::isSynthetic,
+ this::isBridge);
}
private boolean containsAll(int other) {
@@ -163,6 +167,22 @@
return isSet(Constants.ACC_STRICT);
}
+ public void setSynthetic() {
+ set(Constants.ACC_SYNTHETIC);
+ }
+
+ public boolean isSynthetic() {
+ return isSet(Constants.ACC_SYNTHETIC);
+ }
+
+ public void setBridge() {
+ set(Constants.ACC_BRIDGE);
+ }
+
+ public boolean isBridge() {
+ return isSet(Constants.ACC_BRIDGE);
+ }
+
private boolean isSet(int flag) {
return (flags & flag) != 0;
}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
index eaad9a4..a27f4b0 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -953,6 +953,11 @@
flags.setAbstract();
}
break;
+ case 'b':
+ if ((found = acceptString("bridge"))) {
+ flags.setBridge();
+ }
+ break;
case 'f':
if ((found = acceptString("final"))) {
flags.setFinal();
@@ -979,6 +984,8 @@
flags.setStatic();
} else if ((found = acceptString("strictfp"))) {
flags.setStrict();
+ } else if ((found = acceptString("synthetic"))) {
+ flags.setSynthetic();
}
break;
case 't':
diff --git a/src/test/java/com/android/tools/r8/shaking/modifiers/SyntheticAndBridgeModifiersTest.java b/src/test/java/com/android/tools/r8/shaking/modifiers/SyntheticAndBridgeModifiersTest.java
new file mode 100644
index 0000000..7f205f1
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/modifiers/SyntheticAndBridgeModifiersTest.java
@@ -0,0 +1,133 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.shaking.modifiers;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.not;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import java.util.function.Consumer;
+import org.junit.Test;
+
+public class SyntheticAndBridgeModifiersTest extends TestBase {
+
+ static final List<Class<?>> CLASSES = ImmutableList.of(
+ SyntheticAndBridgeModifiersTestClass.class,
+ SyntheticAndBridgeModifiersTestClass.Super.class,
+ SyntheticAndBridgeModifiersTestClass.TestClass.class);
+
+ private void runTest(String keepRules, Consumer<ClassSubject> classConsumer) throws Exception {
+ testForProguard()
+ .addProgramClasses(CLASSES)
+ .addKeepRules(keepRules)
+ .noMinification()
+ .compile()
+ .disassemble()
+ .inspect(inspector -> {
+ ClassSubject clazz =
+ inspector.clazz(SyntheticAndBridgeModifiersTestClass.TestClass.class);
+ assertThat(clazz, isPresent());
+ classConsumer.accept(clazz);
+ });
+
+ testForR8(Backend.DEX)
+ .addProgramClasses(CLASSES)
+ .addKeepRules(keepRules)
+ .noMinification()
+ .compile()
+ .disassemble()
+ .inspect(inspector -> {
+ ClassSubject clazz =
+ inspector.clazz(SyntheticAndBridgeModifiersTestClass.TestClass.class);
+ assertThat(clazz, isPresent());
+ classConsumer.accept(clazz);
+ });
+ }
+
+ private long methodsWithNameStartingWith(ClassSubject clazz, String prefix) {
+ return clazz
+ .allMethods()
+ .stream()
+ .filter(method -> method.getOriginalName().startsWith(prefix))
+ .count();
+ }
+
+ @Test
+ public void testBridgeNotKept() throws Exception {
+ runTest(
+ "-keep class **TestClass$TestClass { java.lang.String x(); }",
+ clazz -> {
+ assertThat(clazz.method("java.lang.Object", "x", ImmutableList.of()), not(isPresent()));
+ assertThat(clazz.method("java.lang.String", "x", ImmutableList.of()), isPresent());
+ });
+ }
+
+ @Test
+ public void testBridgeKept() throws Exception {
+ runTest(
+ "-keep class **TestClass$TestClass { bridge ** x(); }",
+ clazz ->
+ assertThat(clazz.method("java.lang.Object", "x", ImmutableList.of()), isPresent()));
+ }
+
+ @Test
+ public void testSyntheticFieldNotKept() throws Exception {
+ runTest(
+ "-keep class **TestClass$TestClass { java.lang.String x(); }",
+ clazz -> assertEquals(0, clazz.allFields().size()));
+ }
+
+ @Test
+ public void testSyntheticFieldKept() throws Exception {
+ runTest(
+ "-keep class **TestClass$TestClass { synthetic ** this*; }",
+ clazz -> assertEquals(1, clazz.allFields().size()));
+ }
+
+ @Test
+ public void testSyntheticAccessorNotKept() throws Exception {
+ runTest(
+ "-keep class **TestClass$TestClass { java.lang.String x(); }",
+ clazz -> assertEquals(0, methodsWithNameStartingWith(clazz, "access")));
+ }
+
+ @Test
+ public void testSyntheticAccessorKept() throws Exception {
+ runTest(
+ "-keep class **TestClass$TestClass { static synthetic *; }",
+ clazz -> assertEquals(1, methodsWithNameStartingWith(clazz, "access")));
+ }
+}
+
+class SyntheticAndBridgeModifiersTestClass {
+ class Super {
+ Object x() {
+ return null;
+ }
+ }
+
+ // Non static inner classes will have a synthetic field for the outer instance (javac
+ // named this$X).
+ class TestClass extends Super {
+ // This will have a synthetic static accessor (javac named access$XXX).
+ private void test() {
+
+ }
+
+ // javac will create a forwarding bridge with signature 'Object x()'.
+ String x() {
+ return null;
+ }
+ }
+
+ void accessPrivate() {
+ new TestClass().test();
+ }
+}
\ No newline at end of file
diff --git a/tools/apk_masseur.py b/tools/apk_masseur.py
index be380ed..4b13a32 100755
--- a/tools/apk_masseur.py
+++ b/tools/apk_masseur.py
@@ -16,7 +16,12 @@
def parse_options():
parser = optparse.OptionParser(usage=USAGE)
parser.add_option('--dex',
- help='directory with dex files to use instead of those in the apk',
+ help=('directory with dex files to use instead of those in '
+ + 'the apk'),
+ default=None)
+ parser.add_option('--resources',
+ help=('pattern that matches resources to use instead of '
+ + 'those in the apk'),
default=None)
parser.add_option('--out',
help='output file (default ./$(basename <apk>))',
@@ -43,32 +48,39 @@
def findKeystore():
return os.path.join(os.getenv('HOME'), '.android', 'app.keystore')
-def repack(processed_out, original_apk, temp, quiet):
+def repack(apk, processed_out, resources, temp, quiet):
processed_apk = os.path.join(temp, 'processed.apk')
- shutil.copyfile(original_apk, processed_apk)
+ shutil.copyfile(apk, processed_apk)
if not processed_out:
utils.Print('Using original APK as is', quiet=quiet)
return processed_apk
utils.Print(
'Repacking APK with dex files from {}'.format(processed_apk), quiet=quiet)
+
+ # Delete original dex files in APK.
with utils.ChangedWorkingDirectory(temp, quiet=quiet):
cmd = ['zip', '-d', 'processed.apk', '*.dex']
utils.RunCmd(cmd, quiet=quiet)
+
+ # Unzip the jar or zip file into `temp`.
if processed_out.endswith('.zip') or processed_out.endswith('.jar'):
cmd = ['unzip', processed_out, '-d', temp]
if quiet:
cmd.insert(1, '-q')
utils.RunCmd(cmd, quiet=quiet)
processed_out = temp
+
+ # Insert the new dex and resource files from `processed_out` into the APK.
with utils.ChangedWorkingDirectory(processed_out, quiet=quiet):
- dex = glob.glob('*.dex')
- cmd = ['zip', '-u', '-9', processed_apk] + dex
+ dex_files = glob.glob('*.dex')
+ resource_files = glob.glob(resources) if resources else []
+ cmd = ['zip', '-u', '-9', processed_apk] + dex_files + resource_files
utils.RunCmd(cmd, quiet=quiet)
return processed_apk
def sign(unsigned_apk, keystore, temp, quiet):
signed_apk = os.path.join(temp, 'unaligned.apk')
- apk_utils.sign(unsigned_apk, signed_apk, keystore, quiet=quiet)
+ apk_utils.sign_with_apksigner(unsigned_apk, signed_apk, keystore, quiet=quiet)
return signed_apk
def align(signed_apk, temp, quiet):
@@ -88,8 +100,8 @@
return signed_apk
def masseur(
- apk, dex=None, out=None, adb_options=None, keystore=None, install=False,
- quiet=False):
+ apk, dex=None, resources=None, out=None, adb_options=None, keystore=None,
+ install=False, quiet=False):
if not out:
out = os.path.basename(apk)
if not keystore:
@@ -97,7 +109,7 @@
with utils.TempDir() as temp:
processed_apk = None
if dex:
- processed_apk = repack(dex, apk, temp, quiet)
+ processed_apk = repack(apk, dex, resources, temp, quiet)
else:
utils.Print(
'Signing original APK without modifying dex files', quiet=quiet)
diff --git a/tools/apk_utils.py b/tools/apk_utils.py
index af24ec1..41c80c2 100644
--- a/tools/apk_utils.py
+++ b/tools/apk_utils.py
@@ -24,9 +24,9 @@
utils.RunCmd(cmd, quiet=quiet)
def sign_with_apksigner(
- build_tools_dir, unsigned_apk, signed_apk, keystore, password, quiet=False):
+ unsigned_apk, signed_apk, keystore, password='android', quiet=False):
cmd = [
- os.path.join(build_tools_dir, 'apksigner'),
+ os.path.join(utils.ANDROID_BUILD_TOOLS, 'apksigner'),
'sign',
'-v',
'--ks', keystore,
diff --git a/tools/gradle.py b/tools/gradle.py
index 232e96d..8a3502d 100755
--- a/tools/gradle.py
+++ b/tools/gradle.py
@@ -7,7 +7,7 @@
# Will make sure we pulled down gradle before running, and will use the pulled
# down version to have a consistent developer experience.
-import optparse
+import argparse
import os
import subprocess
import sys
@@ -25,10 +25,10 @@
GRADLE = os.path.join(GRADLE_DIR, 'gradle', 'bin', 'gradle')
def ParseOptions():
- result = optparse.OptionParser()
- result.add_option('--java-home', '--java_home',
+ parser = argparse.ArgumentParser(description = 'Call gradle.')
+ parser.add_argument('--java-home', '--java_home',
help='Use a custom java version to run gradle.')
- return result.parse_args()
+ return parser.parse_known_args()
def GetJavaEnv(env):
return dict(env if env else os.environ, JAVA_HOME = jdk.GetJdkHome())
@@ -92,10 +92,9 @@
def Main():
(options, args) = ParseOptions()
- gradle_args = sys.argv[1:]
if options.java_home:
- gradle_args.append('-Dorg.gradle.java.home=' + options.java_home)
- return RunGradle(gradle_args)
+ args.append('-Dorg.gradle.java.home=' + options.java_home)
+ return RunGradle(args)
if __name__ == '__main__':
sys.exit(Main())
diff --git a/tools/run_on_as_app.py b/tools/run_on_as_app.py
index ac27750..0a4f740 100755
--- a/tools/run_on_as_app.py
+++ b/tools/run_on_as_app.py
@@ -178,10 +178,14 @@
def ExtractMarker(apk, temp_dir, options):
r8_jar = os.path.join(temp_dir, 'r8.jar')
+ r8lib_jar = os.path.join(temp_dir, 'r8lib.jar')
- # Use the copy of r8.jar if it is there.
+ # Use the copy of r8.jar or r8lib.jar if one is there.
if os.path.isfile(r8_jar):
cmd = [jdk.GetJavaExecutable(), '-ea', '-jar', r8_jar, 'extractmarker', apk]
+ elif os.path.isfile(r8lib_jar):
+ cmd = [jdk.GetJavaExecutable(), '-ea', '-cp', r8lib_jar,
+ 'com.android.tools.r8.SwissArmyKnife', 'extractmarker', apk]
else:
script = os.path.join(utils.TOOLS_DIR, 'extractmarker.py')
cmd = ['python', script, apk]
@@ -491,7 +495,6 @@
assert os.path.isfile(unsigned_apk)
if options.sign_apks:
apk_utils.sign_with_apksigner(
- utils.ANDROID_BUILD_TOOLS,
unsigned_apk,
signed_apk,
options.keystore,
@@ -531,9 +534,9 @@
# is 'r8'.
entry_point = 'com.android.tools.r8.R8'
- cmd = [jdk.GetJavaExecutable(), '-ea:com.android.tools.r8...', '-cp', r8_jar, entry_point,
- '--release', '--min-api', str(min_sdk), '--pg-conf', proguard_config_file,
- '--lib', android_jar, '--output', zip_dest, apk]
+ cmd = [jdk.GetJavaExecutable(), '-ea:com.android.tools.r8...', '-cp', r8_jar,
+ entry_point, '--release', '--min-api', str(min_sdk), '--pg-conf',
+ proguard_config_file, '--lib', android_jar, '--output', zip_dest, apk]
for android_optional_jar in utils.get_android_optional_jars(compile_sdk):
cmd.append('--lib')
@@ -548,7 +551,8 @@
# Make a copy of the given APK, move the newly generated dex files into the
# copied APK, and then sign the APK.
apk_masseur.masseur(
- apk, dex=zip_dest, out=apk_dest, quiet=options.quiet)
+ apk, dex=zip_dest, resources='META-INF/services/*', out=apk_dest,
+ quiet=options.quiet)
def RunMonkey(app, config, options, apk_dest):
if not WaitForEmulator():