Compare commits

..

45 Commits
beta ... v7.5

Author SHA1 Message Date
James Hamilton
af475c65b4 Update usage.md (#407) 2024-05-28 16:32:58 +02:00
James Hamilton
8d7ddf898c Update foojay resolver plugin 2024-05-27 11:29:21 +02:00
Ellet
f5f2f06334 Add .kotlin from Kotlin 2.0.0 in .gitignore (#406) 2024-05-27 09:05:12 +02:00
James Hamilton
aa43b9dc21 Update versions for 7.5 (#404)
Updates ProGuardCORE + other required dependencies for Kotlin 2 + Java 22; including running tests with Java 22.
2024-05-23 14:01:48 +02:00
Jelle De Coninck
0d9ceb7451 fix lint violations in ConfigurationParserTest 2024-05-06 11:29:28 +02:00
Jelle De Coninck
1d28c11e36 Synchronize keep flags on getter/setter/backing field for kotlin properties 2024-05-06 11:12:38 +02:00
Jelle De Coninck
b85b2cb201 Bump version to 7.5.0-beta01 and add release note 2024-04-30 16:58:42 +02:00
Jelle De Coninck
0c95982828 integrate kotlin 2 support
Summary:
* Update PGC
* Change use of kotlinx.metadata to kotlin.metadata
* Remove unused `KotlinModuleRewriter` class
2024-04-30 16:58:26 +02:00
James Hamilton
38de2e42b2 Update releasenotes.md 2024-04-19 09:50:00 +02:00
James Hamilton
76b2921738 Update ProGuardCORE version (#398) 2024-04-15 17:11:19 +02:00
niccolo.piazzesi
7483ad32f4 Keep name of Kotlin companion classes when corresponding field is kept and resolve potential name collision in KotlinCompanionEqualizer 2024-03-15 12:46:53 +01:00
niccolo.piazzesi
20c99aa3e8 Parse -maximumremovedandroidloglevel 2024-03-07 10:44:31 +01:00
Bentaii
858bcd0eb5 Fix equals to prevent null values (#388)
Switch frame.getSourceFile().equals(String) around to test String against source file value instead of otherway around to prevent  failures when the value is null.
2024-02-21 08:36:51 +01:00
Vincent Rossetto
1c421bf780 Keep Kotlin interface method when default implementation is used 2024-02-13 11:09:20 +01:00
James Hamilton
d4692c3835 Update version number in examples 2024-01-31 18:05:50 +01:00
James Hamilton
712fd768ca Add ability to skip to next option if parse error
Allows providing a function to handle the case of an unknown option.

```
    public static void main(String[] args)
    {
        try
        {
            try (ConfigurationParser parser = new ConfigurationParser(new String[]{"-keep class * {}", "-unknownoption", "-whatisthisoption?"}, System.getProperties()))
            {
                parser.parse(new Configuration(), (option, location) -> {
                    System.out.println("Unknown option: " + option + " @ " + location);
                });
            }
            catch (ParseException ex)
            {
                ex.printStackTrace();
            }
        }
        catch (IOException ex)
        {
            ex.printStackTrace();
        }
    }
```

Output:

```
Unknown option: -unknownoption @ argument number 2
Unknown option: -whatisthisoption? @ argument number 3
```
2024-01-22 14:55:53 +01:00
James Hamilton
c35913c3f2 Add release note 2024-01-19 18:41:05 +01:00
Cristian Garcia
5a8d50090a Replace project usages in ProGuardTask by injected Gradle services (#380)
fixes #254
2024-01-19 18:40:33 +01:00
James Hamilton
06c2d12f7a Bump version number 2024-01-16 09:36:28 +01:00
Oberon Swings
a7265a3536 Conservatively mark wide parameter used if it can only be partially marked. 2024-01-08 08:19:01 +01:00
Ruben Pieters
7160a9e484 Fix nullability flag on named type arguments and reified flag on type parameters 2023-12-07 16:49:40 +01:00
Robin Lefever
12c9c3f23e Fix MemberDescriptorSpecializer checking wrong processing flags
This diff also ensures the `IS_CLASS_AVAILABLE` processing flag is set on classes instead of its members.
This requires us to change usages of this flag in some other places as well.
2023-12-01 14:56:44 +01:00
daphnis.chevreton
a02100cb93 Fix potential access issues when backporting
Backporting nest host/members is not supported and can therefore introduce access issue.
Running the access fixer if `-allowaccessmodification` is set solves this issue by setting appropriate visibility.
2023-11-29 16:14:03 +01:00
Bengt Verscheure
38a0e498b9 Use system-specific line separators 2023-11-24 14:49:44 +01:00
James Hamilton
f6b82b1478 Update example versions to 7.4.1 2023-11-22 11:22:08 +01:00
Bengt Verscheure
789777ded5 Add support for <clinit> in ConfigurationParser 2023-11-17 13:40:46 +01:00
Dimitrios Anyfantakis
e76e47953f Close file handle in ConfigurationWriter 2023-11-03 12:28:07 +01:00
Fergal Whyte
836253f1da Use system-specific line separators in ConfigurationWriterTest 2023-10-26 13:51:26 +02:00
Fergal Whyte
f92fc632b1 Parse alwaysinline and identifiernamestring rules 2023-10-25 17:50:40 +02:00
Fergal Whyte
f5f04cbec5 Fix printing of hash characters in ConfigurationWriter 2023-10-25 17:50:40 +02:00
Fergal Whyte
ce2c8a8b5d Add ConfigurationWriterTest 2023-10-25 17:50:25 +02:00
Fergal Whyte
0032aa037c Prevent NullPointerException when parsing annotations 2023-10-25 17:44:19 +02:00
Fergal Whyte
0070bc9e80 Fix incorrectly configured unit tests 2023-10-25 17:44:11 +02:00
Sebastian Ratz
6f3610bd7b Fix inadvertent closing of System.out when printing configuration (#366)
Co-authored-by: Blend Hamiti <blend.hamiti@guardsquare.com>
2023-10-25 12:15:16 +02:00
James Hamilton
813616d095 Bump version number to 7.4.1 2023-10-25 11:07:22 +02:00
James Hamilton
04123e8f9b Update version number in home.md 2023-10-18 12:01:03 +02:00
James Hamilton
7c153f8eec Enable unit testing of Java 20 and 21 2023-10-12 12:59:11 +02:00
James Hamilton
76cf000348 Update version inside examples 2023-10-12 12:50:05 +02:00
James Hamilton
549e2cde2c Update version number in README 2023-10-12 11:34:29 +02:00
James Hamilton
f04ef27f66 Update version 2023-10-12 11:31:33 +02:00
James Hamilton
bfdfa02f8c Support parsing of wildcard * when used as a field type or method return type in class specifications. 2023-10-11 18:10:05 +02:00
Niccolò Piazzesi
58eae9eed5 Fix broken link to issue tab (#364) 2023-09-27 11:41:44 +02:00
Thomas Vochten
7429219cd2 Allow method from interfaces to be inlined if it is private and is being called from within the interface 2023-09-22 09:05:54 +02:00
Thomas Vochten
8bb7cc0c4b Do not inline methods from interfaces 2023-09-21 05:37:20 +02:00
James Hamilton
9a7a97d1ca Update ProGuardCORE version (#359) 2023-09-01 17:03:40 +02:00
60 changed files with 2211 additions and 973 deletions

1
.gitignore vendored
View File

@@ -6,3 +6,4 @@ build
local.properties
/lib/
docs/html
.kotlin

View File

@@ -109,7 +109,7 @@ buildscript {
mavenCentral()
}
dependencies {
classpath 'com.guardsquare:proguard-gradle:7.1.0'
classpath 'com.guardsquare:proguard-gradle:7.5.0'
}
}
```
@@ -188,7 +188,7 @@ You can publish the artifacts to your local Maven repository using:
## 🤝 Contributing
Contributions, issues and feature requests are welcome in both projects.
Feel free to check the [issues](issues) page and the [contributing
Feel free to check the [issues](https://github.com/Guardsquare/proguard/issues) page and the [contributing
guide](CONTRIBUTING.md) if you would like to contribute.
## 📝 License

View File

@@ -4,7 +4,6 @@ plugins {
id 'maven-publish'
id "org.jetbrains.kotlin.jvm" version "$kotlinVersion"
id 'com.adarshr.test-logger' version '3.0.0'
id 'de.jansauer.printcoverage' version '2.0.0'
id 'jacoco'
id "org.jlleitschuh.gradle.ktlint" version '10.2.1'
}
@@ -24,17 +23,17 @@ dependencies {
implementation "com.google.code.gson:gson:${gsonVersion}"
implementation 'org.apache.logging.log4j:log4j-api:2.19.0'
implementation 'org.apache.logging.log4j:log4j-core:2.19.0'
implementation 'org.json:json:20220924'
implementation 'org.json:json:20231013'
testImplementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"
testImplementation "org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion"
testImplementation 'dev.zacsweers.kctfork:core:0.2.1'
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.5.4' // for kotest framework
testImplementation 'io.kotest:kotest-assertions-core-jvm:5.5.4' // for kotest core jvm assertions
testImplementation 'io.kotest:kotest-property-jvm:5.5.4' // for kotest property test
testImplementation 'io.mockk:mockk:1.13.2' // for mocking
testImplementation 'dev.zacsweers.kctfork:core:0.5.0-alpha07'
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.9.0' // for kotest framework
testImplementation 'io.kotest:kotest-assertions-core-jvm:5.9.0' // for kotest core jvm assertions
testImplementation 'io.kotest:kotest-property-jvm:5.9.0' // for kotest property test
testImplementation 'io.mockk:mockk:1.13.11' // for mocking
testImplementation(testFixtures("com.guardsquare:proguard-core:9.0.8")) {
testImplementation(testFixtures("com.guardsquare:proguard-core:${proguardCoreVersion}")) {
exclude group: 'com.guardsquare', module: 'proguard-core'
}
}
@@ -50,7 +49,7 @@ jar {
// Early access automatic downloads are not yet supported:
// https://github.com/gradle/gradle/issues/14814
// But it will work if e.g. Java N-ea is pre-installed
def javaVersionsForTest = 9..19
def javaVersionsForTest = 9..22
test {
useJUnitPlatform()
@@ -64,8 +63,8 @@ task testAllJavaVersions() { testAllTask ->
useJUnitPlatform()
ignoreFailures = true
// The version of bytebuddy used by mockk only supports Java 20 experimentally so far
if (version == 20) systemProperty 'net.bytebuddy.experimental', true
// The version of bytebuddy used by mockk only supports Java 22 experimentally so far
if (version >= 22) systemProperty 'net.bytebuddy.experimental', true
testAllTask.dependsOn(it)
@@ -86,9 +85,11 @@ jacocoTestReport {
classDirectories.setFrom(classes)
executionData.setFrom project.fileTree(dir: '.', include: '**/build/jacoco/*.exec')
reports {
xml.enabled true
csv.enabled false
html.destination file("${buildDir}/reports/coverage")
xml.required = true
csv.required = false
}
javaVersionsForTest.each { version ->
mustRunAfter "testJava$version"
}
}

View File

@@ -41,9 +41,9 @@ public class ClassSpecification implements Cloneable
public final String extendsAnnotationType;
public final String extendsClassName;
public final List attributeNames = null;
public List fieldSpecifications;
public List methodSpecifications;
public final List<String> attributeNames = null;
public List<MemberSpecification> fieldSpecifications;
public List<MemberSpecification> methodSpecifications;
/**

View File

@@ -116,6 +116,11 @@ public class ConfigurationConstants
public static final String DONT_PROCESS_KOTLIN_METADATA = "-dontprocesskotlinmetadata";
public static final String OPTIMIZE_AGGRESSIVELY = "-optimizeaggressively";
public static final String ALWAYS_INLINE = "-alwaysinline";
public static final String IDENTIFIER_NAME_STRING = "-identifiernamestring";
public static final String MAXIMUM_REMOVED_ANDROID_LOG_LEVEL = "-maximumremovedandroidloglevel";
public static final String ANY_FILE_KEYWORD = "**";
public static final String ANY_ATTRIBUTE_KEYWORD = "*";

View File

@@ -20,13 +20,26 @@
*/
package proguard;
import proguard.classfile.*;
import proguard.classfile.AccessConstants;
import proguard.classfile.ClassConstants;
import proguard.classfile.JavaAccessConstants;
import proguard.classfile.JavaTypeConstants;
import proguard.classfile.TypeConstants;
import proguard.classfile.util.ClassUtil;
import proguard.util.*;
import proguard.util.ListUtil;
import proguard.util.StringUtil;
import java.io.*;
import java.net.*;
import java.util.*;
import java.io.File;
import java.io.IOException;
import java.io.LineNumberReader;
import java.io.StringReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
/**
@@ -139,9 +152,24 @@ public class ConfigurationParser implements AutoCloseable
* @throws IOException if an IO error occurs while reading a configuration.
*/
public void parse(Configuration configuration)
throws ParseException, IOException {
parse(configuration, null);
}
/**
* Parses and returns the configuration.
*
* @param configuration the configuration that is updated as a side-effect.
* @param unknownOptionHandler optional handler for unknown options; if null then a {@link ParseException}
* is thrown when encountering an unknown option.
* @throws ParseException if the any of the configuration settings contains
* a syntax error.
* @throws IOException if an IO error occurs while reading a configuration.
*/
public void parse(Configuration configuration, BiConsumer<String, String> unknownOptionHandler)
throws ParseException, IOException
{
while (nextWord != null)
parseWord: while (nextWord != null)
{
lastComments = reader.lastComments();
@@ -230,9 +258,20 @@ public class ConfigurationParser implements AutoCloseable
else if (ConfigurationConstants.DUMP_OPTION .startsWith(nextWord)) configuration.dump = parseOptionalFile();
else if (ConfigurationConstants.ADD_CONFIGURATION_DEBUGGING_OPTION .startsWith(nextWord)) configuration.addConfigurationDebugging = parseNoArgument(true);
else if (ConfigurationConstants.OPTIMIZE_AGGRESSIVELY .startsWith(nextWord)) configuration.optimizeConservatively = parseNoArgument(false);
else if (ConfigurationConstants.ALWAYS_INLINE .startsWith(nextWord)) parseUnsupportedR8Rules(ConfigurationConstants.ALWAYS_INLINE, true);
else if (ConfigurationConstants.IDENTIFIER_NAME_STRING .startsWith(nextWord)) parseUnsupportedR8Rules(ConfigurationConstants.IDENTIFIER_NAME_STRING, true);
else if (ConfigurationConstants.MAXIMUM_REMOVED_ANDROID_LOG_LEVEL .equals(nextWord)) parseMaximumRemovedAndroidLogLevel();
else
{
throw new ParseException("Unknown option " + reader.locationDescription());
if (unknownOptionHandler != null) {
unknownOptionHandler.accept(nextWord, reader.lineLocationDescription());
while (nextWord != null) {
readNextWord();
if (nextWord != null && nextWord.startsWith("-")) {
continue parseWord;
}
}
} else throw new ParseException("Unknown option " + reader.locationDescription());
}
}
}
@@ -850,7 +889,7 @@ public class ConfigurationParser implements AutoCloseable
int requiredUnsetClassAccessFlags = 0;
// Parse the class annotations and access modifiers until the class keyword.
while (!ConfigurationConstants.CLASS_KEYWORD.equals(nextWord))
while (!ConfigurationConstants.CLASS_KEYWORD.equals(nextWord) && !configurationEnd(true))
{
// Strip the negating sign, if any.
boolean negated =
@@ -1104,12 +1143,25 @@ public class ConfigurationParser implements AutoCloseable
// Parse the class member type and name part.
// Did we get a special wildcard?
if (ConfigurationConstants.ANY_CLASS_MEMBER_KEYWORD.equals(nextWord) ||
ConfigurationConstants.ANY_FIELD_KEYWORD .equals(nextWord) ||
ConfigurationConstants.ANY_METHOD_KEYWORD .equals(nextWord))
boolean isStar = ConfigurationConstants.ANY_CLASS_MEMBER_KEYWORD.equals(nextWord);
boolean isFields = ConfigurationConstants.ANY_FIELD_KEYWORD.equals(nextWord);
boolean isMethods = ConfigurationConstants.ANY_METHOD_KEYWORD.equals(nextWord);
boolean isFieldsOrMethods = isFields || isMethods;
String type = nextWord;
String typeLocation = reader.locationDescription();
// Try to read the class member name; we need to do this now so that we can check the nextWord
// to see if we're parsing a wildcard type.
readNextWord("class member name", false, false, false);
// Is it a wildcard star (short for all members) or is a type wildcard?
boolean isReallyStar = isStar && ConfigurationConstants.SEPARATOR_KEYWORD.equals(nextWord);
if (isFieldsOrMethods || isReallyStar)
{
// Act according to the type of wildcard.
if (ConfigurationConstants.ANY_CLASS_MEMBER_KEYWORD.equals(nextWord))
if (isStar)
{
checkFieldAccessFlags(requiredSetMemberAccessFlags,
requiredUnsetMemberAccessFlags);
@@ -1129,10 +1181,10 @@ public class ConfigurationParser implements AutoCloseable
null,
null));
}
else if (ConfigurationConstants.ANY_FIELD_KEYWORD.equals(nextWord))
else if (isFields)
{
checkFieldAccessFlags(requiredSetMemberAccessFlags,
requiredUnsetMemberAccessFlags);
requiredUnsetMemberAccessFlags);
classSpecification.addField(
new MemberSpecification(requiredSetMemberAccessFlags,
@@ -1141,7 +1193,7 @@ public class ConfigurationParser implements AutoCloseable
null,
null));
}
else if (ConfigurationConstants.ANY_METHOD_KEYWORD.equals(nextWord))
else if (isMethods)
{
checkMethodAccessFlags(requiredSetMemberAccessFlags,
requiredUnsetMemberAccessFlags);
@@ -1154,9 +1206,6 @@ public class ConfigurationParser implements AutoCloseable
null));
}
// We still have to read the closing separator.
readNextWord("separator '" + ConfigurationConstants.SEPARATOR_KEYWORD + "'");
if (!ConfigurationConstants.SEPARATOR_KEYWORD.equals(nextWord))
{
throw new ParseException("Expecting separator '" + ConfigurationConstants.SEPARATOR_KEYWORD +
@@ -1165,37 +1214,37 @@ public class ConfigurationParser implements AutoCloseable
}
else
{
// Make sure we have a proper type.
checkJavaIdentifier("java type");
String type = nextWord;
String typeLocation = reader.locationDescription();
readNextWord("class member name");
String name = nextWord;
checkJavaIdentifier("java type", type, true);
// Did we get just one word before the opening parenthesis?
if (ConfigurationConstants.OPEN_ARGUMENTS_KEYWORD.equals(name))
{
// This must be a constructor then.
// Make sure the type is a proper constructor name.
if (!(type.equals(ClassConstants.METHOD_NAME_INIT) ||
type.equals(externalClassName) ||
type.equals(ClassUtil.externalShortClassName(externalClassName))))
// This must be an initializer then.
// Make sure the type is a proper initializer name.
if (ClassUtil.isInitializer(type))
{
name = type; // This is either `<init>` or `<clinit>`.
type = JavaTypeConstants.VOID;
}
else if (type.equals(externalClassName) ||
type.equals(ClassUtil.externalShortClassName(externalClassName)))
{
name = ClassConstants.METHOD_NAME_INIT;
type = JavaTypeConstants.VOID;
}
else
{
throw new ParseException("Expecting type and name " +
"instead of just '" + type +
"' before " + reader.locationDescription());
}
// Assign the fixed constructor type and name.
type = JavaTypeConstants.VOID;
name = ClassConstants.METHOD_NAME_INIT;
}
else
{
// It's not a constructor.
// It's not an initializer.
// Make sure we have a proper name.
checkJavaIdentifier("class member name");
checkNextWordIsJavaIdentifier("class member name");
// Read the opening parenthesis or the separating
// semi-colon.
@@ -1204,7 +1253,7 @@ public class ConfigurationParser implements AutoCloseable
}
// Check if the type actually contains the use of generics.
// Can not do it right away as we also support "<init>" as a type (see case above).
// Can not do it right away as we also support "<init>" and "<clinit>" as a type (see case above).
if (containsGenerics(type))
{
throw new ParseException("Generics are not allowed (erased) for java type" + typeLocation);
@@ -1285,6 +1334,14 @@ public class ConfigurationParser implements AutoCloseable
"' before " + reader.locationDescription());
}
// Class initializers are not supposed to have any parameters.
if (ClassConstants.METHOD_NAME_CLINIT.equals(name) &&
ClassUtil.internalMethodParameterCount(descriptor) > 0)
{
throw new ParseException("Not expecting method parameters with initializer '" + ClassConstants.METHOD_NAME_CLINIT +
"' before " + reader.locationDescription());
}
// Read the separator after the closing parenthesis.
readNextWord("separator '" + ConfigurationConstants.SEPARATOR_KEYWORD + "'");
@@ -1613,7 +1670,7 @@ public class ConfigurationParser implements AutoCloseable
{
if (checkJavaIdentifiers)
{
checkJavaIdentifier("java type", allowGenerics);
checkNextWordIsJavaIdentifier("java type", allowGenerics);
}
if (replaceSystemProperties)
@@ -1880,30 +1937,34 @@ public class ConfigurationParser implements AutoCloseable
* Checks whether the given word is a valid Java identifier and throws
* a ParseException if it isn't. Wildcard characters are accepted.
*/
private void checkJavaIdentifier(String expectedDescription)
private void checkNextWordIsJavaIdentifier(String expectedDescription)
throws ParseException
{
checkJavaIdentifier(expectedDescription, true);
checkNextWordIsJavaIdentifier(expectedDescription, true);
}
private void checkNextWordIsJavaIdentifier(String expectedDescription, boolean allowGenerics) throws ParseException
{
checkJavaIdentifier(expectedDescription, nextWord, allowGenerics);
}
/**
* Checks whether the given word is a valid Java identifier and throws
* a ParseException if it isn't. Wildcard characters are accepted.
*/
private void checkJavaIdentifier(String expectedDescription, boolean allowGenerics)
throws ParseException
private void checkJavaIdentifier(String expectedDescription, String identifier, boolean allowGenerics)
throws ParseException
{
if (!isJavaIdentifier(nextWord))
if (!isJavaIdentifier(identifier))
{
throw new ParseException("Expecting " + expectedDescription +
" before " + reader.locationDescription());
" before " + reader.locationDescription());
}
if (!allowGenerics && containsGenerics(nextWord))
if (!allowGenerics && containsGenerics(identifier))
{
throw new ParseException("Generics are not allowed (erased) in " + expectedDescription +
" " + reader.locationDescription());
" " + reader.locationDescription());
}
}
@@ -2018,6 +2079,34 @@ public class ConfigurationParser implements AutoCloseable
}
private void parseUnsupportedR8Rules(String option, boolean parseClassSpecification) throws IOException, ParseException
{
readNextWord();
if (parseClassSpecification)
{
parseClassSpecificationArguments();
}
warnUnsupportedR8Option(option);
}
private void parseMaximumRemovedAndroidLogLevel() throws IOException, ParseException {
parseIntegerArgument();
if (!configurationEnd(true)) {
parseClassSpecificationArguments();
}
warnUnsupportedR8Option(ConfigurationConstants.MAXIMUM_REMOVED_ANDROID_LOG_LEVEL);
}
private static void warnUnsupportedR8Option(String option) {
System.out.println("Warning: The R8 option " + option + " is currently not supported by ProGuard.\n" +
"This option will have no effect on the optimized artifact.");
}
/**
* A main method for testing configuration parsing.
*/

View File

@@ -50,7 +50,8 @@ public class ConfigurationWriter implements AutoCloseable
private final PrintWriter writer;
private File baseDir;
private File configurationFile;
private String baseDirName;
/**
@@ -60,7 +61,11 @@ public class ConfigurationWriter implements AutoCloseable
{
this(PrintWriterUtil.createPrintWriterOut(configurationFile));
baseDir = configurationFile.getParentFile();
this.configurationFile = configurationFile;
if (configurationFile.getParentFile() != null)
{
baseDirName = configurationFile.getParentFile().getAbsolutePath() + File.separator;
}
}
@@ -79,7 +84,7 @@ public class ConfigurationWriter implements AutoCloseable
@Override
public void close() throws IOException
{
PrintWriterUtil.closePrintWriter(baseDir, writer);
PrintWriterUtil.closePrintWriter(configurationFile, writer);
}
@@ -799,13 +804,9 @@ public class ConfigurationWriter implements AutoCloseable
String fileName = file.getAbsolutePath();
// See if we can convert the file name into a relative file name.
if (baseDir != null)
if (baseDirName != null && fileName.startsWith(baseDirName))
{
String baseDirName = baseDir.getAbsolutePath() + File.separator;
if (fileName.startsWith(baseDirName))
{
fileName = fileName.substring(baseDirName.length());
}
fileName = fileName.substring(baseDirName.length());
}
return quotedString(fileName);
@@ -819,6 +820,7 @@ public class ConfigurationWriter implements AutoCloseable
{
return string.length() == 0 ||
string.indexOf(' ') >= 0 ||
string.indexOf('#') >= 0 ||
string.indexOf('@') >= 0 ||
string.indexOf('{') >= 0 ||
string.indexOf('}') >= 0 ||
@@ -827,7 +829,7 @@ public class ConfigurationWriter implements AutoCloseable
string.indexOf(':') >= 0 ||
string.indexOf(';') >= 0 ||
string.indexOf(',') >= 0 ? ("'" + string + "'") :
( string );
( string );
}

View File

@@ -48,7 +48,6 @@ import proguard.strip.KotlinAnnotationStripper;
import proguard.util.ConstantMatcher;
import proguard.util.ListParser;
import proguard.util.NameParser;
import proguard.util.PrintWriterUtil;
import proguard.util.StringMatcher;
import proguard.util.kotlin.KotlinUnsupportedVersionChecker;
import proguard.util.kotlin.asserter.KotlinMetadataVerifier;
@@ -297,9 +296,7 @@ public class ProGuard
*/
private void printConfiguration() throws IOException
{
PrintWriter pw = PrintWriterUtil.createPrintWriterOut(configuration.printConfiguration);
try (ConfigurationWriter configurationWriter = new ConfigurationWriter(pw))
try (ConfigurationWriter configurationWriter = new ConfigurationWriter(configuration.printConfiguration))
{
configurationWriter.write(configuration);
}

View File

@@ -314,6 +314,12 @@ public class Backporter implements Pass
appView.programClassPool.classesAccept(new ClassVersionSetter(targetClassVersion));
}
// Backporting may introduce access issues, for example related to nest members/host.
if (configuration.allowAccessModification)
{
appView.programClassPool.classesAccept(new AccessFixer());
}
logger.info(" Number of converted string concatenations: {}", replacedStringConcatCounter.getCount());
logger.info(" Number of converted lambda expressions: {}", lambdaExpressionCounter.getCount());
logger.info(" Number of converted static interface methods: {}", staticInterfaceMethodCounter.getCount());

View File

@@ -72,7 +72,7 @@ implements KotlinMetadataVisitor,
kotlinClassKindMetadata.inlineClassUnderlyingPropertyTypeAccept(clazz, this);
kotlinClassKindMetadata.referencedClass.attributesAccept(annotationCounter.reset());
kotlinClassKindMetadata.flags.common.hasAnnotations = annotationCounter.getCount() > 0;
kotlinClassKindMetadata.flags.hasAnnotations = annotationCounter.getCount() > 0;
}
@Override
@@ -119,28 +119,28 @@ implements KotlinMetadataVisitor,
annotationCounter.reset()
);
kotlinPropertyMetadata.flags.common.hasAnnotations = annotationCounter.getCount() > 0;
kotlinPropertyMetadata.flags.hasAnnotations = annotationCounter.getCount() > 0;
}
else if (kotlinPropertyMetadata.referencedBackingField != null)
{
kotlinPropertyMetadata.referencedBackingField.accept(kotlinPropertyMetadata.referencedBackingFieldClass, annotationCounter);
kotlinPropertyMetadata.flags.common.hasAnnotations = annotationCounter.getCount() > 0;
kotlinPropertyMetadata.flags.hasAnnotations = annotationCounter.getCount() > 0;
}
else
{
kotlinPropertyMetadata.flags.common.hasAnnotations = false;
kotlinPropertyMetadata.flags.hasAnnotations = false;
}
if (kotlinPropertyMetadata.flags.hasGetter && kotlinPropertyMetadata.referencedGetterMethod != null)
if (kotlinPropertyMetadata.referencedGetterMethod != null)
{
kotlinPropertyMetadata.referencedGetterMethod.accept(clazz, annotationCounter.reset());
kotlinPropertyMetadata.getterFlags.common.hasAnnotations = annotationCounter.getCount() > 0;
kotlinPropertyMetadata.getterFlags.hasAnnotations = annotationCounter.getCount() > 0;
}
if (kotlinPropertyMetadata.flags.hasSetter && kotlinPropertyMetadata.referencedSetterMethod != null)
if (kotlinPropertyMetadata.flags.isVar && kotlinPropertyMetadata.referencedSetterMethod != null)
{
kotlinPropertyMetadata.referencedSetterMethod.accept(clazz, annotationCounter.reset());
kotlinPropertyMetadata.setterFlags.common.hasAnnotations = annotationCounter.getCount() > 0;
kotlinPropertyMetadata.setterFlags.hasAnnotations = annotationCounter.getCount() > 0;
}
}
@@ -157,7 +157,7 @@ implements KotlinMetadataVisitor,
kotlinFunctionMetadata.returnTypeAccept( clazz, kotlinMetadata, this);
kotlinFunctionMetadata.referencedMethodAccept(annotationCounter.reset());
kotlinFunctionMetadata.flags.common.hasAnnotations = annotationCounter.getCount() != 0;
kotlinFunctionMetadata.flags.hasAnnotations = annotationCounter.getCount() != 0;
}
// Implementations for KotlinConstructorVisitor.
@@ -172,12 +172,12 @@ implements KotlinMetadataVisitor,
if (kotlinClassKindMetadata.flags.isAnnotationClass)
{
//PROBBUG where are the annotations?
kotlinConstructorMetadata.flags.common.hasAnnotations = false;
kotlinConstructorMetadata.flags.hasAnnotations = false;
}
else
{
kotlinConstructorMetadata.referencedMethodAccept(clazz, annotationCounter.reset());
kotlinConstructorMetadata.flags.common.hasAnnotations = annotationCounter.getCount() != 0;
kotlinConstructorMetadata.flags.hasAnnotations = annotationCounter.getCount() != 0;
}
}
@@ -192,7 +192,7 @@ implements KotlinMetadataVisitor,
kotlinTypeAliasMetadata.expandedTypeAccept( clazz, kotlinDeclarationContainerMetadata, this);
kotlinTypeAliasMetadata.versionRequirementAccept(clazz, kotlinDeclarationContainerMetadata, this);
kotlinTypeAliasMetadata.flags.common.hasAnnotations = !kotlinTypeAliasMetadata.annotations.isEmpty();
kotlinTypeAliasMetadata.flags.hasAnnotations = !kotlinTypeAliasMetadata.annotations.isEmpty();
}
// Implementations for KotlinTypeVisitor.
@@ -202,8 +202,6 @@ implements KotlinMetadataVisitor,
kotlinTypeMetadata.typeArgumentsAccept(clazz, this);
kotlinTypeMetadata.upperBoundsAccept( clazz, this);
kotlinTypeMetadata.abbreviationAccept( clazz, this);
kotlinTypeMetadata.flags.common.hasAnnotations = !kotlinTypeMetadata.annotations.isEmpty();
}
@Override
@@ -213,7 +211,6 @@ implements KotlinMetadataVisitor,
KotlinTypeMetadata kotlinTypeMetadata)
{
kotlinFunctionMetadata.referencedMethodAccept(this.annotationCounter.reset());
kotlinTypeMetadata.flags.common.hasAnnotations = annotationCounter.getParameterAnnotationCount(0) > 0;
}
// Implementations for KotlinTypeParameterVisitor.
@@ -225,8 +222,6 @@ implements KotlinMetadataVisitor,
public void visitAnyTypeParameter(Clazz clazz, KotlinTypeParameterMetadata kotlinTypeParameterMetadata)
{
kotlinTypeParameterMetadata.upperBoundsAccept(clazz, this);
kotlinTypeParameterMetadata.flags.common.hasAnnotations = !kotlinTypeParameterMetadata.annotations.isEmpty();
}
// Implementations for KotlinValueParameterVisitor.
@@ -242,10 +237,10 @@ implements KotlinMetadataVisitor,
kotlinFunctionMetadata,
this);
if (kotlinValueParameterMetadata.flags.common.hasAnnotations)
if (kotlinValueParameterMetadata.flags.hasAnnotations)
{
kotlinFunctionMetadata.referencedMethodAccept(annotationCounter.reset());
kotlinValueParameterMetadata.flags.common.hasAnnotations =
kotlinValueParameterMetadata.flags.hasAnnotations =
annotationCounter.getParameterAnnotationCount(kotlinValueParameterMetadata.index) > 0;
}
}
@@ -261,12 +256,12 @@ implements KotlinMetadataVisitor,
kotlinConstructorMetadata,
this);
if (kotlinValueParameterMetadata.flags.common.hasAnnotations)
if (kotlinValueParameterMetadata.flags.hasAnnotations)
{
if (!kotlinClassKindMetadata.flags.isAnnotationClass)
{
kotlinConstructorMetadata.referencedMethodAccept(clazz, annotationCounter.reset());
kotlinValueParameterMetadata.flags.common.hasAnnotations =
kotlinValueParameterMetadata.flags.hasAnnotations =
annotationCounter.getParameterAnnotationCount(kotlinValueParameterMetadata.index) > 0;
}
}
@@ -283,10 +278,10 @@ implements KotlinMetadataVisitor,
kotlinPropertyMetadata,
this);
if (kotlinValueParameterMetadata.flags.common.hasAnnotations)
if (kotlinValueParameterMetadata.flags.hasAnnotations)
{
kotlinPropertyMetadata.referencedSetterMethod.accept(clazz, annotationCounter.reset());
kotlinValueParameterMetadata.flags.common.hasAnnotations =
kotlinValueParameterMetadata.flags.hasAnnotations =
annotationCounter.getParameterAnnotationCount(kotlinValueParameterMetadata.index) > 0;
}
}

View File

@@ -1,168 +0,0 @@
/*
* ProGuard -- shrinking, optimization, obfuscation, and preverification
* of Java bytecode.
*
* Copyright (c) 2002-2020 Guardsquare NV
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package proguard.io;
import kotlinx.metadata.KmAnnotation;
import kotlinx.metadata.internal.metadata.jvm.deserialization.JvmMetadataVersion;
import kotlinx.metadata.jvm.*;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import proguard.classfile.*;
import proguard.classfile.util.ClassUtil;
import java.io.*;
import java.nio.charset.Charset;
import java.util.*;
public class KotlinModuleRewriter
extends DataEntryCopier
{
private static final Logger logger = LogManager.getLogger(KotlinModuleRewriter.class);
private final ClassPool programClassPool;
public KotlinModuleRewriter(ClassPool programClassPool, Charset charset, DataEntryWriter writer)
{
super(writer);
this.programClassPool = programClassPool;
}
public void read(DataEntry dataEntry) throws IOException
{
super.read(dataEntry);
}
protected void copyData(InputStream inputStream, OutputStream outputStream) throws IOException
{
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
super.copyData(inputStream, byteStream);
byte[] bytes = byteStream.toByteArray();
KotlinModuleMetadata kotlinModuleMetadata = KotlinModuleMetadata.read(bytes);
KmModule kmModule = kotlinModuleMetadata.toKmModule();
ModuleTransformer moduleTransformer = new ModuleTransformer();
kmModule.accept(moduleTransformer);
KotlinModuleMetadata.Writer writer = new KotlinModuleMetadata.Writer();
moduleTransformer.getResult().accept(writer);
byte[] transformedBytes = writer.write(JvmMetadataVersion.INSTANCE.toArray()).getBytes();
outputStream.write(transformedBytes);
}
private class PackageInformation
{
private final String fqName;
private final List<String> fileFacades = new ArrayList<>();
private final Map<String, String> multiFileClassParts = new HashMap<>();
private PackageInformation(String fqName) {this.fqName = fqName;}
public void addToModule(KmModule out)
{
out.visitPackageParts(fqName, fileFacades, multiFileClassParts);
}
}
private class ModuleTransformer extends KmModuleVisitor
{
private final Map<String, PackageInformation> newModuleInfo = new HashMap<>();
private PackageInformation getPackageInformation(String fqName)
{
if (newModuleInfo.containsKey(fqName))
{
return newModuleInfo.get(fqName);
}
else
{
PackageInformation packageInformation = new PackageInformation(fqName);
newModuleInfo.put(fqName, packageInformation);
return packageInformation;
}
}
private PackageInformation getPackageInformation(Clazz clazz)
{
return getPackageInformation(ClassUtil.externalPackageName(ClassUtil.externalClassName(clazz.getName())));
}
public void visitPackageParts(String fqName, List<String> fileFacades, Map<String, String> multiFileClassParts)
{
for (String fileFacade : fileFacades)
{
Clazz newClass = programClassPool.getClass(fileFacade);
if (newClass == null)
{
// This can occur in the case of wrong input modules.
// For instance the kotlin.reflect module declares facades which are actually absent.
continue;
}
getPackageInformation(newClass).fileFacades.add(newClass.getName());
}
for (Map.Entry<String, String> entry : multiFileClassParts.entrySet())
{
Clazz keyClass = programClassPool.getClass(entry.getKey());
Clazz valueClass = programClassPool.getClass(entry.getValue());
if (keyClass == null ||
valueClass == null)
{
continue;
}
getPackageInformation(keyClass).multiFileClassParts.put(keyClass .getName(),
valueClass.getName());
}
}
public KmModule getResult()
{
KmModule out = new KmModule();
for (PackageInformation packageInformation : newModuleInfo.values())
{
packageInformation.addToModule(out);
}
return out;
}
public void visitAnnotation(KmAnnotation annotation)
{
logger.error("Cannot handle annotations yet");
}
public void visitEnd() {}
}
}

View File

@@ -39,12 +39,16 @@ import proguard.classfile.kotlin.KotlinConstants;
import proguard.classfile.kotlin.KotlinDeclarationContainerMetadata;
import proguard.classfile.kotlin.KotlinFunctionMetadata;
import proguard.classfile.kotlin.KotlinMetadata;
import proguard.classfile.kotlin.KotlinPropertyMetadata;
import proguard.classfile.kotlin.KotlinSyntheticClassKindMetadata;
import proguard.classfile.kotlin.visitor.KotlinFunctionToDefaultMethodVisitor;
import proguard.classfile.kotlin.visitor.KotlinFunctionToMethodVisitor;
import proguard.classfile.kotlin.visitor.KotlinFunctionVisitor;
import proguard.classfile.kotlin.visitor.KotlinMetadataVisitor;
import proguard.classfile.kotlin.visitor.KotlinPropertyVisitor;
import proguard.classfile.kotlin.visitor.MemberToKotlinPropertyVisitor;
import proguard.classfile.kotlin.visitor.ReferencedKotlinMetadataVisitor;
import proguard.classfile.kotlin.visitor.filter.KotlinClassFilter;
import proguard.classfile.util.AllParameterVisitor;
import proguard.classfile.visitor.AllMemberVisitor;
import proguard.classfile.visitor.ClassAccessFilter;
@@ -63,14 +67,20 @@ import proguard.classfile.visitor.MultiClassVisitor;
import proguard.classfile.visitor.MultiMemberVisitor;
import proguard.classfile.visitor.NamedMethodVisitor;
import proguard.pass.Pass;
import proguard.util.Processable;
import proguard.util.ProcessingFlagSetter;
import proguard.util.ProcessingFlags;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static proguard.util.ProcessingFlags.DONT_OBFUSCATE;
import static proguard.util.ProcessingFlags.DONT_OPTIMIZE;
import static proguard.util.ProcessingFlags.DONT_SHRINK;
import static proguard.util.ProcessingFlags.DONT_SHRINK_OR_OPTIMIZE_OR_OBFUSCATE;
import static proguard.util.ProcessingFlags.INJECTED;
/**
@@ -124,6 +134,34 @@ public class Marker implements Pass
new ProcessingFlagSetter(ProcessingFlags.DONT_SHRINK | ProcessingFlags.DONT_OPTIMIZE | ProcessingFlags.DONT_OBFUSCATE))));
appView.programClassPool.classesAccept(classVisitor);
appView.libraryClassPool.classesAccept(classVisitor);
// When a property is kept, make sure the getter, setter and backing field all have the same
// keep flags.
ClassVisitor propertyVisitor =
new KotlinClassFilter(
new AllMemberVisitor(
new MemberToKotlinPropertyVisitor(
new KotlinPropertyVisitor() {
public void visitAnyProperty(Clazz clazz,
KotlinDeclarationContainerMetadata kotlinDeclarationContainerMetadata,
KotlinPropertyMetadata kotlinPropertyMetadata) {
List<Processable> processables = Stream.of(kotlinPropertyMetadata.referencedBackingField,
kotlinPropertyMetadata.referencedGetterMethod,
kotlinPropertyMetadata.referencedSetterMethod)
.filter(Objects::nonNull)
.collect(Collectors.toList());
int flags = 0;
for (Processable processable : processables) {
flags |= processable.getProcessingFlags();
}
// Only copy the keep flags.
int copiedFlags = flags & DONT_SHRINK_OR_OPTIMIZE_OR_OBFUSCATE;
processables.forEach(p -> p.setProcessingFlags(p.getProcessingFlags() | copiedFlags));
}
})));
appView.programClassPool.classesAccept(propertyVisitor);
appView.libraryClassPool.classesAccept(propertyVisitor);
}
// Mark members that can be safely used for generalization,
@@ -234,8 +272,7 @@ public class Marker implements Pass
{
// Program classes are always available and safe to generalize/specialize from/to.
ClassVisitor isClassAvailableMarker =
new AllMemberVisitor(
new ProcessingFlagSetter(ProcessingFlags.IS_CLASS_AVAILABLE));
new ProcessingFlagSetter(ProcessingFlags.IS_CLASS_AVAILABLE);
programClassPool.classesAccept(isClassAvailableMarker);

View File

@@ -21,26 +21,33 @@
package proguard.obfuscate.kotlin;
import proguard.classfile.Clazz;
import proguard.classfile.LibraryClass;
import proguard.classfile.LibraryMember;
import proguard.classfile.ProgramClass;
import proguard.classfile.ProgramMember;
import proguard.classfile.kotlin.KotlinClassKindMetadata;
import proguard.classfile.kotlin.KotlinMetadata;
import proguard.classfile.kotlin.visitor.KotlinMetadataVisitor;
import proguard.classfile.visitor.AllFieldVisitor;
import proguard.classfile.visitor.MemberVisitor;
import static proguard.classfile.util.ClassUtil.internalSimpleClassName;
import static proguard.obfuscate.ClassObfuscator.newClassName;
import static proguard.obfuscate.ClassObfuscator.setNewClassName;
import static proguard.obfuscate.MemberObfuscator.newMemberName;
import static proguard.obfuscate.MemberObfuscator.setFixedNewMemberName;
import static proguard.util.ProcessingFlags.DONT_OBFUSCATE;
public class KotlinCompanionEqualizer
implements KotlinMetadataVisitor
{
implements KotlinMetadataVisitor {
@Override
public void visitAnyKotlinMetadata(Clazz clazz, KotlinMetadata kotlinMetadata) {}
public void visitAnyKotlinMetadata(Clazz clazz, KotlinMetadata kotlinMetadata) {
}
@Override
public void visitKotlinClassMetadata(Clazz clazz, KotlinClassKindMetadata kotlinClassKindMetadata)
{
if (kotlinClassKindMetadata.companionObjectName != null)
{
public void visitKotlinClassMetadata(Clazz clazz, KotlinClassKindMetadata kotlinClassKindMetadata) {
if (kotlinClassKindMetadata.companionObjectName != null) {
String newCompanionClassName = newClassName(kotlinClassKindMetadata.referencedCompanionClass);
if (newCompanionClassName == null) return;
@@ -50,13 +57,51 @@ implements KotlinMetadataVisitor
// The Kotlin asserter will check the field name, so will throw the metadata away if
// it wasn't named correctly.
if (newCompanionClassName.contains("$"))
{
// Set a fixed member name to make sure it gets priority when resolving naming conflicts and collecting
// already used member names.
setFixedNewMemberName(kotlinClassKindMetadata.referencedCompanionField,
internalSimpleClassName(newCompanionClassName));
if (newCompanionClassName.contains("$")) {
if ((kotlinClassKindMetadata.referencedCompanionField.getProcessingFlags() & DONT_OBFUSCATE) != 0) {
// The field is to be kept, so the class name should be kept as well.
setNewClassName(kotlinClassKindMetadata.referencedCompanionClass,
newClassName(kotlinClassKindMetadata.referencedClass) + "$" + kotlinClassKindMetadata.companionObjectName);
} else {
final String newCompanionSimpleClassName = internalSimpleClassName(newCompanionClassName);
// Check whether there is already a field with the same fixed new name in the hierarchy.
ConflictingFieldChecker conflictingFieldChecker =
new ConflictingFieldChecker(newCompanionSimpleClassName);
clazz.hierarchyAccept(
false, true, true, false, new AllFieldVisitor(conflictingFieldChecker));
// There is a field in the hierarchy with the same fixed name. Rename the companion
// class and associated field to avoid the class being renamed inconsistently during
// conflict resolution and causing runtime crashes.
if (conflictingFieldChecker.isConflicting) {
newCompanionClassName = newCompanionClassName + "_";
setNewClassName(
kotlinClassKindMetadata.referencedCompanionClass, newCompanionClassName);
}
// Set a fixed member name to make sure it gets priority when resolving naming conflicts and collecting
// already used member names.
setFixedNewMemberName(kotlinClassKindMetadata.referencedCompanionField,
internalSimpleClassName(newCompanionClassName));
}
}
}
}
private static class ConflictingFieldChecker implements MemberVisitor {
public boolean isConflicting = false;
private String newCompanionSimpleClassName;
public ConflictingFieldChecker(String newCompanionSimpleClassName) {
this.newCompanionSimpleClassName = newCompanionSimpleClassName;
}
@Override
public void visitLibraryMember(LibraryClass libraryClass, LibraryMember libraryMember) {}
@Override
public void visitProgramMember(ProgramClass programClass, ProgramMember programMember) {
isConflicting |= newCompanionSimpleClassName.equals(newMemberName(programMember));
}
}
}

View File

@@ -141,7 +141,7 @@ implements MemberVisitor
if (valueClass != null &&
valueClass.extendsOrImplements(ClassUtil.internalClassNameFromClassType(fieldType)) &&
(programField.getProcessingFlags() & ProcessingFlags.IS_CLASS_AVAILABLE) != 0)
(valueClass.getProcessingFlags() & ProcessingFlags.IS_CLASS_AVAILABLE) != 0)
{
logger.debug("MemberDescriptorSpecializer [{}.{} {}] -> {}",
programClass.getName(),
@@ -210,7 +210,7 @@ implements MemberVisitor
if (valueClass != null &&
valueClass.extendsOrImplements(ClassUtil.internalClassNameFromClassType(parameterType)) &&
(programMethod.getProcessingFlags() & ProcessingFlags.IS_CLASS_AVAILABLE) != 0)
(valueClass.getProcessingFlags() & ProcessingFlags.IS_CLASS_AVAILABLE) != 0)
{
logger.debug("MemberDescriptorSpecializer [{}.{}{}]: parameter #{}: {} -> {}",
programClass.getName(),

View File

@@ -22,12 +22,22 @@ package proguard.optimize;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import proguard.classfile.*;
import proguard.classfile.AccessConstants;
import proguard.classfile.Clazz;
import proguard.classfile.Field;
import proguard.classfile.Member;
import proguard.classfile.Method;
import proguard.classfile.ProgramClass;
import proguard.classfile.attribute.CodeAttribute;
import proguard.classfile.constant.*;
import proguard.classfile.constant.AnyMethodrefConstant;
import proguard.classfile.constant.ClassConstant;
import proguard.classfile.constant.FieldrefConstant;
import proguard.classfile.constant.RefConstant;
import proguard.classfile.constant.visitor.ConstantVisitor;
import proguard.classfile.editor.*;
import proguard.classfile.instruction.*;
import proguard.classfile.editor.CodeAttributeEditor;
import proguard.classfile.editor.ConstantPoolEditor;
import proguard.classfile.instruction.ConstantInstruction;
import proguard.classfile.instruction.Instruction;
import proguard.classfile.instruction.visitor.InstructionVisitor;
import proguard.classfile.visitor.ClassVisitor;
import proguard.util.ProcessingFlags;
@@ -200,7 +210,7 @@ implements InstructionVisitor,
// DGD-486: Only generalize members which are always available. Partial replacement of a class that is not
// available on all platforms may result in a VerifyError at runtime.
if (referencedMember != null &&
(referencedMember.getProcessingFlags() & ProcessingFlags.IS_CLASS_AVAILABLE) != 0)
(clazz.getProcessingFlags() & ProcessingFlags.IS_CLASS_AVAILABLE) != 0)
{
clazz.constantPoolEntryAccept(refConstant.u2classIndex, this);
@@ -287,13 +297,14 @@ implements InstructionVisitor,
// Otherwise, look in the super class itself.
// Only consider public classes and methods, to avoid any
// access problems.
// Only consider classes that are marked as available.
if (generalizedClass == null &&
(superClass.getAccessFlags() & AccessConstants.PUBLIC) != 0)
(superClass.getAccessFlags() & AccessConstants.PUBLIC) != 0 &&
(superClass.getProcessingFlags() & ProcessingFlags.IS_CLASS_AVAILABLE) != 0)
{
Method method = superClass.findMethod(memberName, memberType);
if (method != null &&
(method.getAccessFlags() & AccessConstants.PUBLIC) != 0 &&
(method.getProcessingFlags() & ProcessingFlags.IS_CLASS_AVAILABLE) != 0)
(method.getAccessFlags() & AccessConstants.PUBLIC) != 0)
{
// Remember the generalized class and class member.
generalizedClass = superClass;
@@ -308,7 +319,7 @@ implements InstructionVisitor,
Field field = clazz.findField(memberName, memberType);
if (field != null &&
(field.getProcessingFlags() & ProcessingFlags.IS_CLASS_AVAILABLE) != 0)
(clazz.getProcessingFlags() & ProcessingFlags.IS_CLASS_AVAILABLE) != 0)
{
// Remember the generalized class and class member.
generalizedClass = clazz;

View File

@@ -124,6 +124,22 @@ implements MemberVisitor,
-1L : -2L);
}
if (programMethod.processingInfo instanceof ProgramMethodOptimizationInfo
&& parameterSize >= 64) {
int parameterSizesCummulative = 0;
for (int index = 0; parameterSizesCummulative < 64; index++) {
boolean isCategory2 =
((ProgramMethodOptimizationInfo) programMethod.processingInfo).getParameterSize(index)
== 2;
if (parameterSizesCummulative == 63 && isCategory2) {
markParameterUsed(programMethod, 63);
}
parameterSizesCummulative +=
((ProgramMethodOptimizationInfo) programMethod.processingInfo)
.getParameterSize(index);
}
}
// Is it a native method?
if ((accessFlags & AccessConstants.NATIVE) != 0)
{

View File

@@ -652,6 +652,15 @@ implements AttributeVisitor,
AccessConstants.STATIC |
AccessConstants.FINAL)) != 0 &&
DEBUG("Interface?") &&
// Methods in interfaces should only very rarely be inlined
// since this can potentially lead to other methods in the interface
// needing broadened visibility, which can lead to either compilation errors
// during output writing or various issues at runtime.
((programClass.getAccessFlags() & AccessConstants.INTERFACE) == 0 ||
canInlineMethodFromInterface(programClass, programMethod)) &&
DEBUG("Synchronized?") &&
// Only inline the method if it is not synchronized, etc.
@@ -889,6 +898,17 @@ implements AttributeVisitor,
returnChar == TypeConstants.SHORT;
}
/**
* We only inline methods from interfaces if both of these conditions hold:
* - The class that references the method is the interface itself.
* - The method is private.
*/
private boolean canInlineMethodFromInterface(ProgramClass sourceClass, ProgramMethod sourceMethod)
{
return
sourceClass.equals(targetClass) &&
(sourceMethod.getAccessFlags() & AccessConstants.PRIVATE) != 0;
}
/**
* Indicates whether this method should be inlined. Subclasses can overwrite

View File

@@ -1888,7 +1888,7 @@ implements ClassVisitor,
markAsUsed(kotlinPropertyMetadata.setterParameters);
markAsUsed(kotlinPropertyMetadata.type);
if (kotlinPropertyMetadata.flags.common.hasAnnotations &&
if (kotlinPropertyMetadata.flags.hasAnnotations &&
kotlinPropertyMetadata.syntheticMethodForAnnotations != null)
{
// Annotations are placed on a synthetic method (e.g. myProperty$annotations())
@@ -1971,23 +1971,28 @@ implements ClassVisitor,
{
visitAnyFunction(clazz, kotlinDeclarationContainerMetadata, kotlinFunctionMetadata);
// Non-abstract functions in interfaces should have default implementations, so keep it if the
// user kept the original function.
if (isUsed(kotlinFunctionMetadata))
{
if (kotlinDeclarationContainerMetadata.k == KotlinConstants.METADATA_KIND_CLASS &&
((KotlinClassKindMetadata)kotlinDeclarationContainerMetadata).flags.isInterface &&
!kotlinFunctionMetadata.flags.modality.isAbstract &&
(kotlinFunctionMetadata.referencedMethod.getProcessingFlags() & ProcessingFlags.DONT_SHRINK) != 0)
{
kotlinFunctionMetadata.referencedDefaultImplementationMethodAccept(
new MultiMemberVisitor(
ClassUsageMarker.this,
new MemberToClassVisitor(ClassUsageMarker.this)
)
);
}
boolean isInterface =
kotlinDeclarationContainerMetadata.k == KotlinConstants.METADATA_KIND_CLASS
&& ((KotlinClassKindMetadata) kotlinDeclarationContainerMetadata).flags.isInterface
&& !kotlinFunctionMetadata.flags.modality.isAbstract;
if (isUsed(kotlinFunctionMetadata)
&& isInterface
&& (kotlinFunctionMetadata.referencedMethod.getProcessingFlags()
& ProcessingFlags.DONT_SHRINK)
!= 0) {
kotlinFunctionMetadata.referencedDefaultImplementationMethodAccept(
new MultiMemberVisitor(
ClassUsageMarker.this, new MemberToClassVisitor(ClassUsageMarker.this)));
}
// If a default implementation is called directly,
// the interface should be marked as used as well.
if (kotlinFunctionMetadata.referencedDefaultImplementationMethod != null
&& isInterface
&& isUsed(kotlinFunctionMetadata.referencedDefaultImplementationMethod)) {
kotlinFunctionMetadata.referencedMethodAccept(ClassUsageMarker.this);
}
}
// Implementations for KotlinTypeAliasVisitor.

View File

@@ -184,7 +184,6 @@ implements KotlinMetadataVisitor,
{
kotlinPropertyMetadata.getterSignature = null;
kotlinPropertyMetadata.referencedGetterMethod = null;
kotlinPropertyMetadata.flags.hasGetter = false;
}
if (shouldShrinkMetadata(kotlinPropertyMetadata.setterSignature,
@@ -192,7 +191,7 @@ implements KotlinMetadataVisitor,
{
kotlinPropertyMetadata.setterSignature = null;
kotlinPropertyMetadata.referencedSetterMethod = null;
kotlinPropertyMetadata.flags.hasSetter = false;
kotlinPropertyMetadata.flags.isVar = false;
kotlinPropertyMetadata.setterParameters.clear();
}
@@ -206,7 +205,7 @@ implements KotlinMetadataVisitor,
kotlinPropertyMetadata.syntheticMethodForAnnotations = null;
kotlinPropertyMetadata.referencedSyntheticMethodForAnnotations = null;
kotlinPropertyMetadata.referencedSyntheticMethodClass = null;
kotlinPropertyMetadata.flags.common.hasAnnotations = false;
kotlinPropertyMetadata.flags.hasAnnotations = false;
}
if (kotlinPropertyMetadata.syntheticMethodForDelegate != null &&

View File

@@ -30,7 +30,7 @@ import proguard.classfile.util.kotlin.KotlinMetadataInitializer;
import proguard.classfile.visitor.ClassVisitor;
import proguard.pass.Pass;
import static proguard.classfile.util.kotlin.KotlinMetadataInitializer.MAX_SUPPORTED_VERSION;
import static proguard.classfile.io.kotlin.KotlinMetadataWriter.LATEST_STABLE_SUPPORTED;
import static proguard.classfile.util.kotlin.KotlinMetadataInitializer.isSupportedMetadataVersion;
/**
@@ -72,7 +72,7 @@ public class KotlinUnsupportedVersionChecker implements Pass
throw new RuntimeException(
"Unsupported Kotlin metadata version found on class '" + clazz.getName() + "'." +
System.lineSeparator() +
"Kotlin versions up to " + MAX_SUPPORTED_VERSION.major + "." + MAX_SUPPORTED_VERSION.minor + " are supported.");
"Kotlin versions up to " + LATEST_STABLE_SUPPORTED + " are supported.");
}
else
{

View File

@@ -8,9 +8,14 @@
package proguard
import io.kotest.core.spec.style.FreeSpec
import io.kotest.matchers.shouldBe
import io.mockk.spyk
import io.mockk.verify
import proguard.classfile.visitor.ClassPoolVisitor
import proguard.classfile.visitor.ClassVisitor
import proguard.classfile.visitor.MemberCounter
import proguard.classfile.visitor.MemberVisitor
import proguard.testutils.AssemblerSource
import proguard.testutils.ClassPoolBuilder
import proguard.testutils.JavaSource
import testutils.asConfiguration
@@ -106,4 +111,110 @@ class ClassSpecificationVisitorFactoryTest : FreeSpec({
}
}
}
"Given a class with fields of different types" - {
fun createFieldVisitor(config: String, fieldVisitor: MemberVisitor): ClassPoolVisitor =
KeepClassSpecificationVisitorFactory(true, false, false).createClassPoolVisitor(
config.asConfiguration().keep.first(), null, fieldVisitor, null, null
)
val (programClassPool, _) = ClassPoolBuilder.fromSource(
AssemblerSource(
"ClassWithFields.jbc",
"""
public class ClassWithFields {
java.lang.String myField;
int myField;
long myField;
com.example.ClassInAPackage myField;
java.lang.String[] myField;
int[] myField;
com.example.ClassInAPackage[] myField;
ClassWithFields myField;
}
""".trimIndent()
)
)
"Then * should visit any non-primitive, non-array without package separator type fields" {
with(MemberCounter()) {
programClassPool.accept(createFieldVisitor("-keep class ClassWithFields { * myField; }", this))
count shouldBe 1
}
}
"Then ** should visit any non-primitive, non-array separator type fields" {
with(MemberCounter()) {
programClassPool.accept(createFieldVisitor("-keep class ClassWithFields { ** myField; }", this))
count shouldBe 3
}
}
"Then *** should visit all fields" {
with(MemberCounter()) {
programClassPool.accept(createFieldVisitor("-keep class ClassWithFields { *** myField; }", this))
count shouldBe 8
}
}
"Then % should only visit primitive, non-array fields" {
with(MemberCounter()) {
programClassPool.accept(createFieldVisitor("-keep class ClassWithFields { % myField; }", this))
count shouldBe 2
}
}
}
"Given a class with methods of different types" - {
fun createMethodVisitor(config: String, methodVisitor: MemberVisitor): ClassPoolVisitor =
KeepClassSpecificationVisitorFactory(true, false, false).createClassPoolVisitor(
config.asConfiguration().keep.first(), null, null, methodVisitor, null
)
val (programClassPool, _) = ClassPoolBuilder.fromSource(
AssemblerSource(
"ClassWithFields.jbc",
"""
public class ClassWithFields {
public void myMethod(java.lang.String);
public void myMethod(int);
public void myMethod(long);
public void myMethod(com.example.ClassInAPackage);
public void myMethod(java.lang.String[]);
public void myMethod(int[]);
public void myMethod(com.example.ClassInAPackage[]);
public void myMethod(ClassWithFields);
}
""".trimIndent()
)
)
"Then * should visit any non-primitive, non-array without package separator type methods" {
with(MemberCounter()) {
programClassPool.accept(createMethodVisitor("-keep class ClassWithFields { void myMethod(*); }", this))
count shouldBe 1
}
}
"Then ** should visit any non-primitive, non-array separator type methods" {
with(MemberCounter()) {
programClassPool.accept(createMethodVisitor("-keep class ClassWithFields { void myMethod(**); }", this))
count shouldBe 3
}
}
"Then *** should visit all methods" {
with(MemberCounter()) {
programClassPool.accept(createMethodVisitor("-keep class ClassWithFields { void myMethod(***); }", this))
count shouldBe 8
}
}
"Then % should only visit primitive, non-array methods" {
with(MemberCounter()) {
programClassPool.accept(createMethodVisitor("-keep class ClassWithFields { void myMethod(%); }", this))
count shouldBe 2
}
}
}
})

View File

@@ -9,6 +9,13 @@ package proguard
import io.kotest.assertions.throwables.shouldThrow
import io.kotest.core.spec.style.FreeSpec
import io.kotest.matchers.shouldBe
import io.kotest.matchers.shouldNotBe
import io.kotest.matchers.string.shouldContain
import proguard.classfile.AccessConstants.PUBLIC
import testutils.asConfiguration
import java.io.ByteArrayOutputStream
import java.io.PrintStream
/**
* Some simple testcases to catch special cases when parsing the Configuration.
@@ -57,6 +64,31 @@ class ConfigurationParserTest : FreeSpec({
parseConfiguration("-keep class * { public protected <methods>; }")
}
"Keep rule with ClassName should be valid" {
val configuration = parseConfiguration("-keep class ClassName { ClassName(); }")
val keep = configuration.keep.single().methodSpecifications.single()
keep.name shouldBe "<init>"
keep.descriptor shouldBe "()V"
}
"Keep rule with ClassName and external class com.example.ClassName should be valid" {
val configuration = parseConfiguration("-keep class com.example.ClassName { ClassName(); }")
val keep = configuration.keep.single().methodSpecifications.single()
keep.name shouldBe "<init>"
keep.descriptor shouldBe "()V"
}
"Keep rule with <clinit> should be valid" {
val configuration = parseConfiguration("-keep class ** { <clinit>(); }")
val keep = configuration.keep.single().methodSpecifications.single()
keep.name shouldBe "<clinit>"
keep.descriptor shouldBe "()V"
}
"Keep rule with <clinit> and non-empty argument list should throw ParseException" {
shouldThrow<ParseException> { parseConfiguration("-keep class * { void <clinit>(int) }") }
}
"Keep rule with * member wildcard and return type should be valid" {
parseConfiguration("-keep class * { java.lang.String *; }")
}
@@ -77,4 +109,323 @@ class ConfigurationParserTest : FreeSpec({
shouldThrow<ParseException> { parseConfiguration("-keep class * { <methods>(); }") }
}
}
"A ParseException should be thrown with invalid annotation config at the end of the file" - {
// This is a parse error without any further config after it.
val configStr = ("-keep @MyAnnotation @ThisShouldBeInterfaceKeyword")
"Then the option should throw a ParseException" {
shouldThrow<ParseException> {
configStr.asConfiguration()
}
}
}
"Testing -alwaysinline parsing" - {
"Given an empty configuration" - {
val savedPrintStream = System.out
val customOutputStream = ByteArrayOutputStream()
System.setOut(PrintStream(customOutputStream))
parseConfiguration("")
"The option does not print anything" {
customOutputStream.toString() shouldContain ""
System.setOut(savedPrintStream)
}
}
"Given a configuration with -alwaysinline" - {
val savedPrintStream = System.out
val customOutputStream = ByteArrayOutputStream()
System.setOut(PrintStream(customOutputStream))
parseConfiguration(
"""-alwaysinline class * {
@org.chromium.build.annotations.AlwaysInline *;
}
"""
)
"The option prints out a warning" {
customOutputStream.toString() shouldContain "Warning: The R8 option -alwaysinline is currently not supported by ProGuard.\n" +
"This option will have no effect on the optimized artifact."
System.setOut(savedPrintStream)
}
}
"Given a configuration with -alwaysinline with no class specification" - {
"The parsing should throw an exception" {
shouldThrow<ParseException> { parseConfiguration("-alwaysinline") }
}
}
}
"Testing -identifiernamestring parsing" - {
"Given an empty configuration" - {
val savedPrintStream = System.out
val customOutputStream = ByteArrayOutputStream()
System.setOut(PrintStream(customOutputStream))
parseConfiguration("")
"The option does not print anything" {
customOutputStream.toString() shouldContain ""
System.setOut(savedPrintStream)
}
}
"Given a configuration with -identifiernamestring" - {
val savedPrintStream = System.out
val customOutputStream = ByteArrayOutputStream()
System.setOut(PrintStream(customOutputStream))
parseConfiguration(
"""-identifiernamestring class * {
@org.chromium.build.annotations.IdentifierNameString *;
}
"""
)
"The option prints out a warning" {
customOutputStream.toString() shouldContain "Warning: The R8 option -identifiernamestring is currently not supported by ProGuard.\n" +
"This option will have no effect on the optimized artifact."
System.setOut(savedPrintStream)
}
}
"Given a configuration with -identifiernamestring with no class specification" - {
"The parsing should throw an exception" {
shouldThrow<ParseException> { parseConfiguration("-identifiernamestring") }
}
}
}
"Testing -maximumremovedandroidloglevel parsing" - {
"Given an empty configuration" - {
val savedPrintStream = System.out
val customOutputStream = ByteArrayOutputStream()
System.setOut(PrintStream(customOutputStream))
parseConfiguration("")
"The option does not print anything" {
customOutputStream.toString() shouldContain ""
System.setOut(savedPrintStream)
}
}
"Given a configuration with -maximumremovedandroidloglevel without a class specification" - {
val savedPrintStream = System.out
val customOutputStream = ByteArrayOutputStream()
System.setOut(PrintStream(customOutputStream))
parseConfiguration("-maximumremovedandroidloglevel 1")
"The option prints out a warning" {
customOutputStream.toString() shouldContain "Warning: The R8 option -maximumremovedandroidloglevel is currently not supported by ProGuard.\n" +
"This option will have no effect on the optimized artifact."
System.setOut(savedPrintStream)
}
}
"Given a configuration with -maximumremovedandroidloglevel with a class specification" - {
val savedPrintStream = System.out
val customOutputStream = ByteArrayOutputStream()
System.setOut(PrintStream(customOutputStream))
parseConfiguration(
"""
-maximumremovedandroidloglevel 1 @org.chromium.build.annotations.DoNotStripLogs class ** {
<methods>;
}
""".trimIndent(),
)
"The option prints out a warning" {
customOutputStream.toString() shouldContain "Warning: The R8 option -maximumremovedandroidloglevel is currently not supported by ProGuard.\n" +
"This option will have no effect on the optimized artifact."
System.setOut(savedPrintStream)
}
}
}
"Wildcard type tests" - {
class TestConfig(
val configOption: String,
classSpecificationConfig: String,
private val classSpecificationGetter: Configuration.() -> List<ClassSpecification>?
) {
private val configuration: Configuration by lazy {
"$configOption $classSpecificationConfig".asConfiguration()
}
val classSpecifications: List<ClassSpecification>? get() = classSpecificationGetter.invoke(configuration)
}
fun generateTestCases(clSpec: String): List<TestConfig> = listOf(
TestConfig("-keep", clSpec) { keep },
TestConfig("-assumenosideeffects", clSpec) { assumeNoSideEffects },
TestConfig("-assumenoexternalsideeffects", clSpec) { assumeNoExternalSideEffects },
TestConfig("-assumenoescapingparameters", clSpec) { assumeNoEscapingParameters },
TestConfig("-assumenoexternalreturnvalues", clSpec) { assumeNoExternalReturnValues },
TestConfig("-assumevalues", clSpec) { assumeValues },
)
"Test wildcard matches all methods and fields" {
val testConfigurations = generateTestCases("class Foo { *; }") + generateTestCases("class Foo { <fields>; <methods>; }")
for (testConfig in testConfigurations) {
val classSpecifications = testConfig.classSpecifications
val methodSpecification = classSpecifications?.single()?.methodSpecifications?.single()
methodSpecification shouldNotBe null
methodSpecification?.requiredSetAccessFlags shouldBe 0
methodSpecification?.name shouldBe null
methodSpecification?.descriptor shouldBe null
val fieldSpecification = classSpecifications?.single()?.fieldSpecifications?.single()
fieldSpecification shouldNotBe null
fieldSpecification?.requiredSetAccessFlags shouldBe 0
fieldSpecification?.name shouldBe null
fieldSpecification?.descriptor shouldBe null
}
}
"Test wildcard method return type" {
val testConfigurations = generateTestCases("class Foo { * bar(); }")
for (testConfig in testConfigurations) {
val classSpecifications = testConfig.classSpecifications
val methodSpecification = classSpecifications?.single()?.methodSpecifications?.single()
methodSpecification?.requiredSetAccessFlags shouldBe 0
methodSpecification?.name shouldBe "bar"
methodSpecification?.descriptor shouldBe "()L*;"
val fieldSpecification = classSpecifications?.single()?.fieldSpecifications
fieldSpecification shouldBe null
}
}
"Test wildcard method return type with access modifier" {
val testConfigurations = generateTestCases("class Foo { public * bar(); }")
for (testConfig in testConfigurations) {
val classSpecifications = testConfig.classSpecifications
val methodSpecification = classSpecifications?.single()?.methodSpecifications?.single()
methodSpecification?.requiredSetAccessFlags shouldBe PUBLIC
methodSpecification?.name shouldBe "bar"
methodSpecification?.descriptor shouldBe "()L*;"
val fieldSpecification = classSpecifications?.single()?.fieldSpecifications
fieldSpecification shouldBe null
}
}
"Test wildcard field type" {
val testConfigurations = generateTestCases("class Foo { * bar; }")
for (testConfig in testConfigurations) {
val classSpecifications = testConfig.classSpecifications
val methodSpecification = classSpecifications?.single()?.methodSpecifications
methodSpecification shouldBe null
val fieldSpecification = classSpecifications?.single()?.fieldSpecifications?.single()
fieldSpecification?.requiredSetAccessFlags shouldBe 0
fieldSpecification?.name shouldBe "bar"
fieldSpecification?.descriptor shouldBe "L*;"
}
}
"Test wildcard field type with access modifier" {
val testConfigurations = generateTestCases("class Foo { public * bar; }")
for (testConfig in testConfigurations) {
val classSpecifications = testConfig.classSpecifications
val methodSpecification = classSpecifications?.single()?.methodSpecifications
methodSpecification shouldBe null
val fieldSpecification = classSpecifications?.single()?.fieldSpecifications?.single()
fieldSpecification?.requiredSetAccessFlags shouldBe PUBLIC
fieldSpecification?.name shouldBe "bar"
fieldSpecification?.descriptor shouldBe "L*;"
}
}
"Test all type wildcard field" {
val testConfigurations = generateTestCases("class Foo { *** bar; }")
for (testConfig in testConfigurations) {
val classSpecifications = testConfig.classSpecifications
val methodSpecification = classSpecifications?.single()?.methodSpecifications
methodSpecification shouldBe null
val fieldSpecification = classSpecifications?.single()?.fieldSpecifications?.single()
fieldSpecification?.requiredSetAccessFlags shouldBe 0
fieldSpecification?.name shouldBe "bar"
fieldSpecification?.descriptor shouldBe "L***;"
}
}
"Test all type wildcard field type with access modifier" {
val testConfigurations = generateTestCases("class Foo { public *** bar; }")
for (testConfig in testConfigurations) {
val classSpecifications = testConfig.classSpecifications
val methodSpecification = classSpecifications?.single()?.methodSpecifications
methodSpecification shouldBe null
val fieldSpecification = classSpecifications?.single()?.fieldSpecifications?.single()
fieldSpecification?.requiredSetAccessFlags shouldBe PUBLIC
fieldSpecification?.name shouldBe "bar"
fieldSpecification?.descriptor shouldBe "L***;"
}
}
"Test all type wildcard method return type" {
val testConfigurations = generateTestCases("class Foo { *** bar(); }")
for (testConfig in testConfigurations) {
val classSpecifications = testConfig.classSpecifications
val methodSpecification = classSpecifications?.single()?.methodSpecifications?.single()
methodSpecification?.requiredSetAccessFlags shouldBe 0
methodSpecification?.name shouldBe "bar"
methodSpecification?.descriptor shouldBe "()L***;"
val fieldSpecification = classSpecifications?.single()?.fieldSpecifications
fieldSpecification shouldBe null
}
}
"Test all type wildcard method return type with access modifier" {
val testConfigurations = generateTestCases("class Foo { public *** bar(); }")
for (testConfig in testConfigurations) {
val classSpecifications = testConfig.classSpecifications
val methodSpecification = classSpecifications?.single()?.methodSpecifications?.single()
methodSpecification?.requiredSetAccessFlags shouldBe PUBLIC
methodSpecification?.name shouldBe "bar"
methodSpecification?.descriptor shouldBe "()L***;"
val fieldSpecification = classSpecifications?.single()?.fieldSpecifications
fieldSpecification shouldBe null
}
}
"Test concrete wildcard field type" {
val testConfigurations = generateTestCases("class Foo { java.lang.String bar; }")
for (testConfig in testConfigurations) {
val classSpecifications = testConfig.classSpecifications
val methodSpecification = classSpecifications?.single()?.methodSpecifications
methodSpecification shouldBe null
val fieldSpecification = classSpecifications?.single()?.fieldSpecifications?.single()
fieldSpecification?.requiredSetAccessFlags shouldBe 0
fieldSpecification?.name shouldBe "bar"
fieldSpecification?.descriptor shouldBe "Ljava/lang/String;"
}
}
"Test concrete wildcard method return type" {
val testConfigurations = generateTestCases("class Foo { java.lang.String bar(); }")
for (testConfig in testConfigurations) {
val classSpecifications = testConfig.classSpecifications
val methodSpecification = classSpecifications?.single()?.methodSpecifications?.single()
methodSpecification?.requiredSetAccessFlags shouldBe 0
methodSpecification?.name shouldBe "bar"
methodSpecification?.descriptor shouldBe "()Ljava/lang/String;"
val fieldSpecification = classSpecifications?.single()?.fieldSpecifications
fieldSpecification shouldBe null
}
}
}
})

View File

@@ -0,0 +1,152 @@
package proguard
import io.kotest.core.spec.style.FreeSpec
import io.kotest.matchers.shouldBe
import io.kotest.matchers.string.shouldContain
import java.io.PrintWriter
import java.io.StringWriter
/**
* Test printing of the configuration (-printconfiguration option).
*/
class ConfigurationWriterTest : FreeSpec({
val EOL = System.lineSeparator()
fun printConfiguration(rules: String): String {
val out = StringWriter()
val configuration = Configuration()
ConfigurationParser(rules, "", null, System.getProperties()).use {
it.parse(configuration)
}
ConfigurationWriter(PrintWriter(out)).use {
it.write(configuration)
}
return out.toString().trim()
}
"Keep rules tests" - {
"Keep class constructor should be kept" {
val rules = "-keep class * {$EOL <init>();$EOL}"
val out = printConfiguration(rules)
out shouldBe rules
}
"Keep class initializer should be kept" {
val rules = "-keep class * {$EOL <clinit>();$EOL}"
val out = printConfiguration(rules)
val expected = "-keep class * {$EOL void <clinit>();$EOL}"
out shouldBe expected
}
"Keep class initializer should respect allowobfuscation flag" {
val rules = "-keep,allowobfuscation class ** extends com.example.A {$EOL <clinit>();$EOL}"
val out = printConfiguration(rules)
val expected = "-keep,allowobfuscation class ** extends com.example.A {$EOL void <clinit>();$EOL}"
out shouldBe expected
}
}
"Hash character handling tests" - {
"Option parameters with hash characters should be quoted" {
printConfiguration("-keystorepassword '#tester'") shouldBe "-keystorepassword '#tester'"
}
"Comments should not be quoted" {
printConfiguration("# comment$EOL-keep class **") shouldBe "# comment$EOL-keep class **"
}
"Hash characters in comments should not be quoted" {
printConfiguration("# #comment$EOL-keep class **") shouldBe "# #comment$EOL-keep class **"
}
}
"Given a -dontnote rule specifying a class name" - {
val rules = "-dontnote com.example.MyClass"
"When the rule is parsed and printed out again" - {
val out = printConfiguration(rules)
"Then the printed rule should be the same as the given rule" {
out shouldBe rules
}
}
}
"Given a -dontnote rule specifying a class name with wildcards" - {
val rules = "-dontnote com.example.**"
"When the rule is parsed and printed out again" - {
val out = printConfiguration(rules)
"Then the printed rule should be the same as the given rule" {
out shouldBe rules
}
}
}
"Given a -addconfigurationdebugging rule" - {
val rules = "-addconfigurationdebugging"
"When the rules are parsed and printed out again" - {
val out = printConfiguration(rules)
"Then the rules should be present in the output" {
out shouldContain rules
}
}
}
"Given an -optimizeaggressively rule" - {
val rules = "-optimizeaggressively"
"When the rule is parsed and printed out again" - {
val out = printConfiguration(rules)
"Then the rule should be present in the output" {
out shouldBe rules
}
}
}
"Given a -alwaysinline rule" - {
val rules = "-alwaysinline class ** {*;}"
"When the rule is parsed and printed out again" - {
val out = printConfiguration(rules)
"Then the rule should be not be present in the output" {
out shouldBe ""
}
}
"When the rule does not exist it shouldn't be printed out" - {
val out = printConfiguration("")
"Then the rule should not be present in the output" {
out shouldBe ""
}
}
}
"Given a -identifiernamestring rule" - {
val rules = "-identifiernamestring class ** {*;}"
"When the rule is parsed and printed out again" - {
val out = printConfiguration(rules)
"Then the rule should be not be present in the output" {
out shouldBe ""
}
}
"When the rule does not exist it shouldn't be printed out" - {
val out = printConfiguration("")
"Then the rule should not be present in the output" {
out shouldBe ""
}
}
}
})

View File

@@ -3,6 +3,7 @@ package proguard.optimize
import io.kotest.core.spec.style.FreeSpec
import io.kotest.matchers.shouldBe
import proguard.classfile.AccessConstants
import proguard.classfile.ClassPool
import proguard.classfile.Clazz
import proguard.classfile.Member
import proguard.classfile.attribute.visitor.AllAttributeVisitor
@@ -26,7 +27,67 @@ import proguard.util.ProcessingFlagSetter
import proguard.util.ProcessingFlags.IS_CLASS_AVAILABLE
class MemberDescriptorSpecializerTest : FreeSpec({
"Given a method with a more general parameter type than its use" - {
fun specializeMemberDescriptors(
programClassPool: ClassPool,
libraryClassPool: ClassPool,
) {
// Mark all program classes as available.
programClassPool.classesAccept(ProcessingFlagSetter(IS_CLASS_AVAILABLE))
// Setup the OptimizationInfo on the classes
val keepMarker = KeepMarker()
libraryClassPool.classesAccept(keepMarker)
libraryClassPool.classesAccept(AllMemberVisitor(keepMarker))
programClassPool.classesAccept(ProgramClassOptimizationInfoSetter())
programClassPool.classesAccept(AllMemberVisitor(ProgramMemberOptimizationInfoSetter()))
// Create the optimization as in Optimizer
val fillingOutValuesClassVisitor = ClassVisitorFactory {
val valueFactory: ValueFactory = ParticularValueFactory()
val storingInvocationUnit: InvocationUnit = StoringInvocationUnit(
valueFactory,
true,
true,
true
)
ClassAccessFilter(
0, AccessConstants.SYNTHETIC,
AllMethodVisitor(
AllAttributeVisitor(
DebugAttributeVisitor(
"Filling out fields, method parameters, and return values",
PartialEvaluator(
valueFactory, storingInvocationUnit,
true
)
)
)
)
)
}
programClassPool.classesAccept(fillingOutValuesClassVisitor.createClassVisitor())
// Specialize class member descriptors, based on partial evaluation.
programClassPool.classesAccept(
AllMemberVisitor(
OptimizationInfoMemberFilter(
MemberDescriptorSpecializer(
true,
true,
true,
null,
null,
null
)
)
)
)
}
"Given a method with a more general program class pool parameter type than its use" - {
val (programClassPool, libraryClassPool) = ClassPoolBuilder.fromSource(
JavaSource(
"Test.java",
@@ -48,60 +109,7 @@ class MemberDescriptorSpecializerTest : FreeSpec({
)
"When specializing the member descriptors" - {
// Mark all members as available.
programClassPool.classesAccept(AllMemberVisitor(ProcessingFlagSetter(IS_CLASS_AVAILABLE)))
// Setup the OptimizationInfo on the classes
val keepMarker = KeepMarker()
libraryClassPool.classesAccept(keepMarker)
libraryClassPool.classesAccept(AllMemberVisitor(keepMarker))
programClassPool.classesAccept(ProgramClassOptimizationInfoSetter())
programClassPool.classesAccept(AllMemberVisitor(ProgramMemberOptimizationInfoSetter()))
// Create the optimization as in Optimizer
val fillingOutValuesClassVisitor = ClassVisitorFactory {
val valueFactory: ValueFactory = ParticularValueFactory()
val storingInvocationUnit: InvocationUnit = StoringInvocationUnit(
valueFactory,
true,
true,
true
)
ClassAccessFilter(
0, AccessConstants.SYNTHETIC,
AllMethodVisitor(
AllAttributeVisitor(
DebugAttributeVisitor(
"Filling out fields, method parameters, and return values",
PartialEvaluator(
valueFactory, storingInvocationUnit,
true
)
)
)
)
)
}
programClassPool.classesAccept(fillingOutValuesClassVisitor.createClassVisitor())
// Specialize class member descriptors, based on partial evaluation.
programClassPool.classesAccept(
AllMemberVisitor(
OptimizationInfoMemberFilter(
MemberDescriptorSpecializer(
true,
true,
true,
null,
null,
null
)
)
)
)
specializeMemberDescriptors(programClassPool, libraryClassPool)
"Then the member descriptor should be correctly specialised" {
lateinit var memberDescriptor: String
@@ -122,4 +130,87 @@ class MemberDescriptorSpecializerTest : FreeSpec({
}
}
}
"Given a field with a more general program class pool parameter type than its use" - {
val (programClassPool, libraryClassPool) = ClassPoolBuilder.fromSource(
JavaSource(
"Test.java",
"""
public class Test {
static Bar myField = null;
public static void main(String[] args) {
myField = new Foo();
}
}
class Bar { }
class Foo extends Bar { }
""".trimIndent()
)
)
"When specializing the member descriptors" - {
specializeMemberDescriptors(programClassPool, libraryClassPool)
"Then the member descriptor should be correctly specialised" {
lateinit var memberDescriptor: String
programClassPool.classAccept(
"Test",
AllMemberVisitor(
MemberNameFilter(
"myField*",
object : MemberVisitor {
override fun visitAnyMember(clazz: Clazz, member: Member) {
memberDescriptor = member.getDescriptor(clazz)
}
}
)
)
)
memberDescriptor shouldBe "LFoo;"
}
}
}
"Given a field with a more general library class pool parameter type than its use" - {
val (programClassPool, libraryClassPool) = ClassPoolBuilder.fromSource(
JavaSource(
"Test.java",
"""
public class Test {
static java.lang.Object myField = null;
public static void main(String[] args) {
myField = new java.lang.StringBuffer();
}
}
""".trimIndent()
)
)
"When specializing the member descriptors" - {
specializeMemberDescriptors(programClassPool, libraryClassPool)
"Then the member descriptor should be correctly specialised" {
lateinit var memberDescriptor: String
programClassPool.classAccept(
"Test",
AllMemberVisitor(
MemberNameFilter(
"myField*",
object : MemberVisitor {
override fun visitAnyMember(clazz: Clazz, member: Member) {
memberDescriptor = member.getDescriptor(clazz)
}
}
)
)
)
// Library classes are not marked as available by default. Therefore, they are not specialized.
memberDescriptor shouldBe "Ljava/lang/Object;"
}
}
}
})

View File

@@ -0,0 +1,78 @@
package proguard.optimize.peephole
import io.kotest.core.spec.IsolationMode
import io.kotest.core.spec.style.FreeSpec
import io.kotest.matchers.ints.shouldBeGreaterThan
import proguard.classfile.Clazz
import proguard.classfile.Method
import proguard.classfile.ProgramClass
import proguard.classfile.ProgramMethod
import proguard.classfile.attribute.CodeAttribute
import proguard.classfile.attribute.visitor.AllAttributeVisitor
import proguard.classfile.visitor.AllMethodVisitor
import proguard.classfile.visitor.ClassVisitor
import proguard.classfile.visitor.MultiClassVisitor
import proguard.optimize.info.ProgramClassOptimizationInfoSetter
import proguard.optimize.info.ProgramMemberOptimizationInfoSetter
import proguard.testutils.ClassPoolBuilder
import proguard.testutils.JavaSource
import testutils.RequiresJavaVersion
@RequiresJavaVersion(9)
class MethodInlinerJava9Test : FreeSpec({
isolationMode = IsolationMode.InstancePerTest
"Given a method calling a private method in the same interface" - {
val (programClassPool, _) = ClassPoolBuilder.fromSource(
JavaSource(
"Foo.java",
"""interface Foo {
default void f1() {
f2();
}
private static void f2() {
StringBuilder sb = new StringBuilder();
sb.append(System.currentTimeMillis());
System.out.println(sb.toString());
}
}""",
)
)
val clazz = programClassPool.getClass("Foo") as ProgramClass
val method = clazz.findMethod("f1", "()V") as ProgramMethod
val codeAttr = method.attributes.filterIsInstance<CodeAttribute>()[0]
val lengthBefore = codeAttr.u4codeLength
// Initialize optimization info (used when inlining).
val optimizationInfoInitializer: ClassVisitor = MultiClassVisitor(
ProgramClassOptimizationInfoSetter(),
AllMethodVisitor(
ProgramMemberOptimizationInfoSetter()
)
)
programClassPool.classesAccept(optimizationInfoInitializer)
// Create a mock method inliner which always returns true.
val methodInliner = object : MethodInliner(false, true, true) {
override fun shouldInline(clazz: Clazz?, method: Method?, codeAttribute: CodeAttribute?): Boolean = true
}
"Then the interface method is inlined" {
programClassPool.classesAccept(
AllMethodVisitor(
AllAttributeVisitor(
methodInliner
)
)
)
val lengthAfter = codeAttr.u4codeLength
lengthAfter shouldBeGreaterThan lengthBefore
}
}
})

View File

@@ -282,6 +282,60 @@ class MethodInlinerTest : FreeSpec({
}
}
}
"Given a method calling another non-private method in an interface" - {
val (programClassPool, _) = ClassPoolBuilder.fromSource(
JavaSource(
"Foo.java",
"""interface Foo {
default void f1() {
f2();
}
static void f2() {
StringBuilder sb = new StringBuilder();
sb.append(System.currentTimeMillis());
System.out.println(sb.toString());
}
}"""
)
)
val clazz = programClassPool.getClass("Foo") as ProgramClass
val method = clazz.findMethod("f1", "()V") as ProgramMethod
val codeAttr = method.attributes.filterIsInstance<CodeAttribute>()[0]
val lengthBefore = codeAttr.u4codeLength
// Initialize optimization info (used when inlining).
val optimizationInfoInitializer: ClassVisitor = MultiClassVisitor(
ProgramClassOptimizationInfoSetter(),
AllMethodVisitor(
ProgramMemberOptimizationInfoSetter()
)
)
programClassPool.classesAccept(optimizationInfoInitializer)
// Create a mock method inliner which always returns true.
val methodInliner = object : MethodInliner(false, true, true) {
override fun shouldInline(clazz: Clazz?, method: Method?, codeAttribute: CodeAttribute?): Boolean = true
}
"Then the interface method is not inlined" {
programClassPool.classesAccept(
AllMethodVisitor(
AllAttributeVisitor(
methodInliner
)
)
)
val lengthAfter = codeAttr.u4codeLength
lengthAfter shouldBeExactly lengthBefore
}
}
})
private fun printProgramMethodInstructions(

View File

@@ -10,11 +10,17 @@ import io.kotest.matchers.shouldNot
import io.kotest.matchers.shouldNotBe
import proguard.AppView
import proguard.Configuration
import proguard.classfile.Clazz
import proguard.classfile.Member
import proguard.classfile.attribute.annotation.visitor.AllElementValueVisitor
import proguard.classfile.attribute.visitor.AllAttributeVisitor
import proguard.classfile.kotlin.visitor.ReferencedKotlinMetadataVisitor
import proguard.classfile.util.EnumFieldReferenceInitializer
import proguard.classfile.visitor.AllMethodVisitor
import proguard.classfile.visitor.MemberVisitor
import proguard.classfile.visitor.MultiClassVisitor
import proguard.classfile.visitor.NamedClassVisitor
import proguard.classfile.visitor.NamedMethodVisitor
import proguard.testutils.ClassPoolBuilder
import proguard.testutils.JavaSource
import proguard.testutils.KotlinSource
@@ -212,4 +218,41 @@ class ClassUsageMarkerTest : StringSpec({
programClassPool.classAccept("Test", MultiClassVisitor(classUsageMarker, AllMethodVisitor(classUsageMarker)))
}
}
"Given a Kotlin interface with default method implementation" {
val (programClassPool, _) = ClassPoolBuilder.fromSource(
KotlinSource(
"Interface.kt",
"""
package test;
interface Interface {
fun foo() : Int {
return 42;
}
}
""".trimIndent(),
),
)
// Necessary to force marking methods that are not actually used and have not been processed by the Marker.
class CustomMarker(var marker: SimpleUsageMarker) : MemberVisitor {
override fun visitAnyMember(clazz: Clazz, member: Member) {
marker.markAsUsed(member)
}
}
val usageMarker = SimpleUsageMarker()
val classUsageMarker = ClassUsageMarker(usageMarker)
// Mark the classes as used.
programClassPool.classesAccept(classUsageMarker)
// Mark the default implementation as used.
programClassPool.accept(NamedClassVisitor(NamedMethodVisitor("foo", null, CustomMarker(usageMarker)), "test/Interface\$DefaultImpls"))
// Process Kotlin metadata: this should cause the interface method to be kept as well.
programClassPool.classesAccept(ReferencedKotlinMetadataVisitor(classUsageMarker))
val fooInterface = programClassPool.getClass("test/Interface").findMethod("foo", null)
fooInterface should beMarkedWith(usageMarker)
}
})

View File

@@ -1,7 +1,9 @@
This page lists all available options for ProGuard, grouped logically.
!!! android R8
R8, the default Android shrinker, is compatible with ProGuard keep rules.
R8, the default Android shrinker, is compatible with ProGuard keep rules. Both ProGuard and R8 were designed for app optimization, and although they employ minimal obfuscation techniques, they are not security tools and do not harden applications effectively against reverse engineering and tampering.
[DexGuard](https://hubs.la/Q02yCV0F0) is a protection tool for Android apps that is backward compatible with R8, making it easy to upgrade your R8 configuration with multi-layered security protections to your unprotected mobile application. Learn more in our blog: [Android Security and Obfuscation Realities of R8](https://hubs.la/Q02yCTlt0).
## Input/Output Options {: #iooptions}

View File

@@ -1,4 +1,4 @@
Welcome to the manual for **ProGuard** version 7.3 ([what's new?](releasenotes.md)).
Welcome to the manual for **ProGuard** version 7.5 ([what's new?](releasenotes.md)).
ProGuard is an open-sourced Java class file shrinker, optimizer, obfuscator, and
preverifier. As a result, ProGuard processed applications and libraries are smaller and faster.

View File

@@ -1,3 +1,41 @@
## Version 7.5.0
### Kotlin support
- Add support for Kotlin 2.0. (#376)
### Java support
- Add support for Java 22. (#387)
### Bugfixes
- Prevent unwanted name collision leading to missing methods in Kotlin DefaultImpls classes.
- Prevent `ParseException` when consumer rules contain `-maximumremovedandroidloglevel` rules.
## Version 7.4.2
### Bugfixes
- Fix potential access issues when backporting.
- Fix potential NoClassDefFoundError when using type specialization optimization. (#373)
- Improve processing of Kotlin metadata flags to prevent unnecessary null checks for consumers of protected library artifacts.
- Prevent potential `StackGeneralizationException` during optimization when processing methods with many arguments.
### Added
- `ProGuardTask` support for Gradle configuration cache. (#254)
## Version 7.4.1
### Bugfixes
- Fix inadvertent closing of System.out when printing configuration. (#365)
### Added
- Support for parsing of `<clinit>` methods without specifying the return type in class specifications.
## Version 7.4
### Java support
@@ -11,9 +49,20 @@
### Bugfixes
- Fix "NoClassDefFoundError: Failed resolution of: Lorg/apache/logging/log4j/LogManager" when using GSON optimization or `-addconfigurationdebugging`. (#326)
- Don't drop Record attribute for records with no components. (proguard-core#118)
- Fix potential duplication class when name obfuscating Kotlin multi-file facades.
- Do not inline interface methods during optimization to avoid compilation errors during output writing due to an interface method being made package visible.
### Added
- Support parsing of wildcard `*` when used as a field type or method return type in class specifications.
## Version 7.3.2
### Java support
- Add support for Java 20. (#294)
### Improved
- Merge classes only when `-optimizeaggressively` is set.

View File

@@ -7,12 +7,12 @@ buildscript {
google()
}
dependencies {
classpath 'com.guardsquare:proguard-gradle:7.3.1'
classpath 'com.guardsquare:proguard-gradle:7.5.0'
}
}
plugins {
id 'org.jetbrains.kotlin.jvm' version '1.8.0'
id 'org.jetbrains.kotlin.jvm' version '2.0.0'
id 'application'
}

View File

@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
zipStorePath=wrapper/dists

View File

@@ -205,6 +205,12 @@ set -- \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.

View File

@@ -14,7 +14,7 @@
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@@ -25,7 +25,7 @@
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
if "%DIRNAME%"=="" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@@ -40,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
if %ERRORLEVEL% equ 0 goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
@@ -75,13 +75,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal

View File

@@ -2,11 +2,12 @@ import proguard.gradle.ProGuardTask
buildscript {
repositories {
mavenLocal()
mavenCentral()
google()
}
dependencies {
classpath 'com.guardsquare:proguard-gradle:7.3.0'
classpath 'com.guardsquare:proguard-gradle:7.5.0'
}
}
@@ -32,6 +33,12 @@ dependencies {
ext.baseCoordinates = "${project.name}-${project.version}"
java {
toolchain {
languageVersion = JavaLanguageVersion.of(22)
}
}
tasks.register('proguard', ProGuardTask) {
configuration file('proguard.pro')

View File

Binary file not shown.

View File

@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.2-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env sh
#!/bin/sh
#
# Copyright 2015 the original author or authors.
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -17,67 +17,101 @@
#
##############################################################################
##
## Gradle start up script for UN*X
##
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
APP_BASE_NAME=${0##*/}
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
MAX_FD=maximum
warn () {
echo "$*"
}
} >&2
die () {
echo
echo "$*"
echo
exit 1
}
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
@@ -87,9 +121,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD="$JAVA_HOME/bin/java"
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
@@ -98,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
@@ -106,80 +140,95 @@ location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=`expr $i + 1`
done
case $i in
0) set -- ;;
1) set -- "$args0" ;;
2) set -- "$args0" "$args1" ;;
3) set -- "$args0" "$args1" "$args2" ;;
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=`save "$@"`
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"

View File

@@ -1,89 +1,89 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

View File

@@ -1,10 +1,11 @@
buildscript {
repositories {
mavenLocal()
mavenCentral()
google()
}
dependencies {
classpath("com.guardsquare:proguard-gradle:7.3.0")
classpath("com.guardsquare:proguard-gradle:7.5.0")
}
}

View File

Binary file not shown.

View File

@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.2-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env sh
#!/bin/sh
#
# Copyright 2015 the original author or authors.
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -17,67 +17,101 @@
#
##############################################################################
##
## Gradle start up script for UN*X
##
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
APP_BASE_NAME=${0##*/}
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
MAX_FD=maximum
warn () {
echo "$*"
}
} >&2
die () {
echo
echo "$*"
echo
exit 1
}
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
@@ -87,9 +121,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD="$JAVA_HOME/bin/java"
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
@@ -98,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
@@ -106,80 +140,101 @@ location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=`expr $i + 1`
done
case $i in
0) set -- ;;
1) set -- "$args0" ;;
2) set -- "$args0" "$args1" ;;
3) set -- "$args0" "$args1" "$args2" ;;
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=`save "$@"`
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"

View File

@@ -14,7 +14,7 @@
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@@ -25,7 +25,7 @@
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
if "%DIRNAME%"=="" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@@ -40,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
if %ERRORLEVEL% equ 0 goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
@@ -75,13 +75,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal

View File

@@ -2,11 +2,12 @@ import proguard.gradle.ProGuardTask
buildscript {
repositories {
mavenLocal()
mavenCentral()
google()
}
dependencies {
classpath 'com.guardsquare:proguard-gradle:7.3.0'
classpath 'com.guardsquare:proguard-gradle:7.5.0'
}
}

View File

Binary file not shown.

View File

@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.2-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env sh
#!/bin/sh
#
# Copyright 2015 the original author or authors.
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -17,67 +17,101 @@
#
##############################################################################
##
## Gradle start up script for UN*X
##
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
APP_BASE_NAME=${0##*/}
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
MAX_FD=maximum
warn () {
echo "$*"
}
} >&2
die () {
echo
echo "$*"
echo
exit 1
}
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
@@ -87,9 +121,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD="$JAVA_HOME/bin/java"
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
@@ -98,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
@@ -106,80 +140,101 @@ location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=`expr $i + 1`
done
case $i in
0) set -- ;;
1) set -- "$args0" ;;
2) set -- "$args0" "$args1" ;;
3) set -- "$args0" "$args1" "$args2" ;;
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=`save "$@"`
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"

View File

@@ -14,7 +14,7 @@
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@@ -25,7 +25,7 @@
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
if "%DIRNAME%"=="" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@@ -40,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
if %ERRORLEVEL% equ 0 goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
@@ -75,13 +75,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal

View File

@@ -48,11 +48,12 @@ dependencies {
exclude module: 'proguard-gradle'
exclude module: 'proguard-base'
}
testImplementation 'io.kotest:kotest-runner-junit5-jvm:4.6.0' // for kotest framework
testImplementation 'io.kotest:kotest-assertions-core-jvm:4.6.0' // for kotest core jvm assertions
testImplementation 'io.kotest:kotest-property-jvm:4.6.0' // for kotest property test
testImplementation "io.mockk:mockk:1.12.0"
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.9.0' // for kotest framework
testImplementation 'io.kotest:kotest-assertions-core-jvm:5.9.0' // for kotest core jvm assertions
testImplementation 'io.kotest:kotest-property-jvm:5.9.0' // for kotest property test
testImplementation 'io.mockk:mockk:1.13.11' // for mocking
testImplementation "commons-io:commons-io:2.8.0"
fatJar project(":base")

View File

@@ -24,6 +24,7 @@ import groovy.lang.Closure;
import org.gradle.api.DefaultTask;
import org.gradle.api.file.*;
import org.gradle.api.logging.*;
import org.gradle.api.model.ObjectFactory;
import org.gradle.api.tasks.*;
import org.gradle.api.tasks.Optional;
import org.gradle.util.GradleVersion;
@@ -32,6 +33,7 @@ import proguard.classfile.*;
import proguard.classfile.util.ClassUtil;
import proguard.util.ListUtil;
import javax.inject.Inject;
import java.io.*;
import java.net.*;
import java.util.*;
@@ -44,6 +46,11 @@ import java.util.*;
@CacheableTask
public abstract class ProGuardTask extends DefaultTask
{
@Inject
protected abstract ObjectFactory getObjectFactory();
@Inject
protected abstract ProjectLayout getProjectLayout();
// Accumulated input and output, for the sake of Gradle's lazy file
// resolution and lazy task execution.
private final List inJarFiles = new ArrayList();
@@ -63,16 +70,12 @@ public abstract class ProGuardTask extends DefaultTask
private ClassSpecification classSpecification;
public static final String DEFAULT_CONFIG_RESOURCE_PREFIX = "/lib";
private final String resolvedConfigurationFileNamePrefix = getProject().file(DEFAULT_CONFIG_RESOURCE_PREFIX).toString();
private final String resolvedConfigurationFileNamePrefix = getProjectLayout().files(DEFAULT_CONFIG_RESOURCE_PREFIX).getSingleFile().toString();
// INTERNAL USE ONLY - write extra data entries to this jar
private File extraJar;
public ProGuardTask() {
if (GradleVersion.current().compareTo(GradleVersion.version("7.4")) >= 0) {
// This method was added in Gradle 7.4
notCompatibleWithConfigurationCache("https://github.com/Guardsquare/proguard/issues/254");
}
}
// Gradle task inputs and outputs, because annotations on the List fields
@@ -82,26 +85,26 @@ public abstract class ProGuardTask extends DefaultTask
@Classpath
public FileCollection getInJarFileCollection()
{
return getProject().files(inJarFiles);
return getProjectLayout().files(inJarFiles);
}
@OutputFiles
public FileCollection getOutJarFileCollection()
{
return getProject().files(outJarFiles);
return getProjectLayout().files(outJarFiles);
}
@Classpath
public FileCollection getLibraryJarFileCollection()
{
return getProject().files(libraryJarFiles);
return getProjectLayout().files(libraryJarFiles);
}
@InputFiles
@PathSensitive(PathSensitivity.RELATIVE)
public FileCollection getConfigurationFileCollection()
{
return getProject().files(configurationFiles);
return getProjectLayout().files(configurationFiles);
}
// Convenience methods to retrieve settings from outside the task.
@@ -672,7 +675,7 @@ public abstract class ProGuardTask extends DefaultTask
public void printseeds(Object printSeeds)
throws ParseException
{
configuration.printSeeds = getProject().file(printSeeds);
configuration.printSeeds = getProjectLayout().files(printSeeds).getSingleFile();
}
@Optional
@@ -710,7 +713,7 @@ public abstract class ProGuardTask extends DefaultTask
public void printusage(Object printUsage)
throws ParseException
{
configuration.printUsage = getProject().file(printUsage);
configuration.printUsage = getProjectLayout().files(printUsage).getSingleFile();
}
@Optional
@@ -925,7 +928,7 @@ public abstract class ProGuardTask extends DefaultTask
public void printmapping(Object printMapping)
throws ParseException
{
configuration.printMapping = getProject().file(printMapping);
configuration.printMapping = getProjectLayout().files(printMapping).getSingleFile();
}
@Optional
@@ -937,7 +940,7 @@ public abstract class ProGuardTask extends DefaultTask
public void applymapping(Object applyMapping)
throws ParseException
{
configuration.applyMapping = getProject().file(applyMapping);
configuration.applyMapping = getProjectLayout().files(applyMapping).getSingleFile();
}
public void obfuscationdictionary(Object obfuscationDictionary)
@@ -1219,7 +1222,7 @@ public abstract class ProGuardTask extends DefaultTask
public void keystore(Object keyStore)
{
configuration.keyStores =
extendList(configuration.keyStores, getProject().file(keyStore));
extendList(configuration.keyStores, getProjectLayout().files(keyStore).getSingleFile());
}
public void keystorepassword(String keyStorePassword)
@@ -1321,7 +1324,7 @@ public abstract class ProGuardTask extends DefaultTask
throws ParseException
{
configuration.printConfiguration =
getProject().file(printConfiguration);
getProjectLayout().files(printConfiguration).getSingleFile();
}
@Optional
@@ -1346,7 +1349,7 @@ public abstract class ProGuardTask extends DefaultTask
public void dump(Object dump)
throws ParseException
{
configuration.dump = getProject().file(dump);
configuration.dump = getProjectLayout().files(dump).getSingleFile();
}
@Optional
@@ -1490,7 +1493,7 @@ public abstract class ProGuardTask extends DefaultTask
Object fileObject = configurationFiles.get(index);
ConfigurableFileCollection fileCollection =
getProject().files(fileObject);
getObjectFactory().fileCollection().from(fileObject);
// Parse the configuration as a collection of files.
Iterator<File> files = fileCollection.iterator();
@@ -1595,7 +1598,7 @@ public abstract class ProGuardTask extends DefaultTask
Map filterArgs,
boolean output)
{
ConfigurableFileCollection fileCollection = getProject().files(files);
ConfigurableFileCollection fileCollection = getObjectFactory().fileCollection().from(files);
if (classPath == null)
{
@@ -2299,7 +2302,7 @@ public abstract class ProGuardTask extends DefaultTask
*/
private URL url(Object fileObject) throws MalformedURLException
{
File file = getProject().file(fileObject);
File file = getProjectLayout().files(fileObject).getSingleFile();
return
fileObject instanceof URL ? (URL)fileObject :
fileObject instanceof String &&

View File

@@ -46,6 +46,7 @@ class GradlePluginIntegrationTest : FreeSpec({
.forwardOutput()
.withArguments("proguard")
.withPluginClasspath()
.withGradleVersion("7.4")
.withProjectDir(projectRoot)
.build()
@@ -55,8 +56,6 @@ class GradlePluginIntegrationTest : FreeSpec({
}
}
// ProguardTask is still incompatible, but will not fail the build. Once the issue is resolved, this test will fail
// and should be updated to demonstrate compatibility.
"ProguardTask will not fail when configuration cache is used" - {
val projectRoot = tempdir()
val fixture = File(GradlePluginIntegrationTest::class.java.classLoader.getResource("application").path)
@@ -76,13 +75,12 @@ class GradlePluginIntegrationTest : FreeSpec({
"Then the build was successful with configuration cache" {
result.output shouldContain "SUCCESSFUL"
// E.g., "Configuration cache entry discarded with 4 problems."
result.output shouldContain "Configuration cache entry discarded with"
result.output shouldContain "Configuration cache entry stored."
}
}
}
"gradle plugin can be configured via #configOption" - {
"gradle plugin can be configured via #configOption" {
include(testConfigOption("proguard"))
include(testConfigOption("proguardWithConfigFile"))
include(testConfigOption("proguardWithGeneratedConfigFile"))
@@ -94,6 +92,7 @@ fun testConfigOption(task: String) = funSpec {
GradleRunner.create()
.forwardOutput()
.withArguments(tasks.asList())
.withGradleVersion("7.4")
.withPluginClasspath()
.withProjectDir(projectRoot)
.build()

View File

@@ -32,7 +32,7 @@ import testutils.TestPluginClasspath
class ProguardCacheRelocateabilityIntegrationTest : FreeSpec({
"proguard task can be relocated" - {
"proguard task can be relocated" {
val cacheDir = tempdir()
val fixture = File(ProguardCacheRelocateabilityIntegrationTest::class.java.classLoader.getResource("spring-boot").path)
@@ -49,6 +49,7 @@ class ProguardCacheRelocateabilityIntegrationTest : FreeSpec({
GradleRunner.create()
.forwardOutput()
.withArguments("proguard", "--build-cache")
.withGradleVersion("7.4")
.withPluginClasspath()
.withProjectDir(originalDir)
.build()
@@ -56,6 +57,7 @@ class ProguardCacheRelocateabilityIntegrationTest : FreeSpec({
val result2 = GradleRunner.create()
.forwardOutput()
.withArguments("proguard", "--build-cache")
.withGradleVersion("7.4")
.withPluginClasspath()
.withProjectDir(relocatedDir)
.build()

View File

@@ -1,9 +1,9 @@
proguardVersion = 7.4.0-beta01
proguardVersion = 7.5.0
# The version of ProGuardCORE that sub-projects are built with
proguardCoreVersion = 9.0.9
proguardCoreVersion = 9.1.4
gsonVersion = 2.9.0
kotlinVersion = 1.7.20
kotlinVersion = 2.0.0
target = 1.8
# Optionally compile the WTK plugin.

View File

Binary file not shown.

View File

@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

275
gradlew vendored
View File

@@ -1,7 +1,7 @@
#!/usr/bin/env sh
#!/bin/sh
#
# Copyright 2015 the original author or authors.
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -17,67 +17,101 @@
#
##############################################################################
##
## Gradle start up script for UN*X
##
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
APP_BASE_NAME=${0##*/}
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
MAX_FD=maximum
warn () {
echo "$*"
}
} >&2
die () {
echo
echo "$*"
echo
exit 1
}
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MSYS* | MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
@@ -87,9 +121,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD="$JAVA_HOME/bin/java"
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
@@ -98,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
@@ -106,80 +140,101 @@ location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=`expr $i + 1`
done
case $i in
0) set -- ;;
1) set -- "$args0" ;;
2) set -- "$args0" "$args1" ;;
3) set -- "$args0" "$args1" "$args2" ;;
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=`save "$@"`
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"

14
gradlew.bat vendored
View File

@@ -14,7 +14,7 @@
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@@ -25,7 +25,7 @@
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
if "%DIRNAME%"=="" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@@ -40,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
if %ERRORLEVEL% equ 0 goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
@@ -75,13 +75,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal

View File

@@ -116,7 +116,7 @@ public class FrameRemapper implements MappingProcessor
if (fieldInfo.matches(originalType))
{
originalFieldFrames.add(new FrameInfo(fieldInfo.originalClassName,
obfuscatedFrame.getSourceFile().equals("Unknown Source") ?
"Unknown Source".equals(obfuscatedFrame.getSourceFile()) ?
"Unknown Source" :
sourceFileName(fieldInfo.originalClassName),
obfuscatedFrame.getLineNumber(),
@@ -186,7 +186,7 @@ public class FrameRemapper implements MappingProcessor
}
originalMethodFrames.add(new FrameInfo(methodInfo.originalClassName,
obfuscatedFrame.getSourceFile().equals("Unknown Source") ?
"Unknown Source".equals(obfuscatedFrame.getSourceFile()) ?
"Unknown Source" :
sourceFileName(methodInfo.originalClassName),
lineNumber,

View File

@@ -2,7 +2,7 @@ pluginManagement {
resolutionStrategy {
eachPlugin {
if (requested.id.id == 'com.github.johnrengelman.shadow') {
useVersion '5.2.0'
useVersion '8.1.1'
}
if (requested.id.id == 'io.github.gradle-nexus.publish-plugin') {
useVersion '1.1.0'
@@ -11,6 +11,10 @@ pluginManagement {
}
}
plugins {
id 'org.gradle.toolchains.foojay-resolver-convention' version '0.8.0'
}
rootProject.name = 'proguard'
include 'base'