Skip to content

Commit

Permalink
Add Build Transformer stripping Sync methods (#6069)
Browse files Browse the repository at this point in the history
  • Loading branch information
cmelchior committed Aug 14, 2018
1 parent 047b208 commit 7d85693
Show file tree
Hide file tree
Showing 29 changed files with 1,336 additions and 7 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Expand Up @@ -6,6 +6,10 @@
* [ObjectServer] Added `SyncSession.isConnected()`.
* [ObjectServer] Added support for observing connection changes for a session using `SyncSession.addConnectionChangeListener()` and `SyncSession.removeConnectionChangeListener()`.

### Bug Fixes

* Methods and classes requiring synchronized Realms have been removed from the standard AAR package. They are now only visible when enabling synchronized Realms in Gradle. The methods and classes will still be visible in the source files and docs, but annotated with `@ObjectServer` (#5799).

### Internal

* Updated to Object Store commit: 97fd03819f398b3c81c8b007feaca8636629050b
Expand Down
9 changes: 9 additions & 0 deletions build.gradle
Expand Up @@ -47,11 +47,19 @@ task installTransformer(type:GradleBuild) {
tasks = ['publishToMavenLocal']
}

task installBuildTransformer(type:GradleBuild) {
group = 'Install'
description = 'Install the jar realm-library-build-transformer into mavenLocal()'
buildFile = file('library-build-transformer/build.gradle')
tasks = ['publishToMavenLocal']
}

task assembleRealm(type:GradleBuild) {
group = 'Build'
description = 'Assemble the Realm project'
dependsOn installAnnotations
dependsOn installTransformer
dependsOn installBuildTransformer
buildFile = file('realm/build.gradle')
tasks = ['assemble', 'javadocJar', 'sourcesJar']
if (project.hasProperty('buildTargetABIs')) {
Expand Down Expand Up @@ -137,6 +145,7 @@ task installRealm(type:GradleBuild) {
group = 'Install'
description = 'Install the artifacts of Realm libraries into mavenLocal()'
dependsOn installTransformer
dependsOn installBuildTransformer
buildFile = file('realm/build.gradle')
tasks = ['publishToMavenLocal']
if (project.hasProperty('buildTargetABIs')) {
Expand Down
1 change: 1 addition & 0 deletions library-build-transformer/.gitignore
@@ -0,0 +1 @@
out/
41 changes: 41 additions & 0 deletions library-build-transformer/README.md
@@ -0,0 +1,41 @@
# Library Transformer

This project contains a transformer that removes all classes, methods and fields annotated with a
given annotation.

This can be used to emulate Kotlin extension methods in cases where separating the code into flavour
folders is not feasible, like e.g. when the `Realm` class is shared between the `base` and
`objectServer` flavour.

## Usage

Register the transformer as normal and provide it with the flavor to strip and annotation to detect

```
import io.realm.buildtransformer.RealmBuildTransformer
android.registerTransform(new RealmBuildTransformer("base", "io.realm.internal.annotations.ObjectServer", [
"explicit_files_to_remove"
]))
```

It is also possible to provide a specific list of files that will be removed whether or not they
have the annotation. This is used to remove some files created by the annotation processor that do
not carry over annotations.

## Warning

There are no checks in place with regard to it being safe or not to remove classes and methods, so
only apply the transformer when it is safe to do so (i.e. the classes/methods/fields are not in use).
Any errors will only be caught at runtime when the actual code is accessed.

## Known limitations

* If all constructors are stripped by this transformer, a new default constructor will not be
created. This will result in invalid byte code being generated.

* If the top-level class is removed, all inner classes, enums and interfaces must also be annotated,
otherwise they are not removed, resulting in valid bytecode being generated.

* Annotations on super classes will also remove subclasses, but only the first level of inheritance.

* Single enum values cannot be stripped, only the entire enum class.
106 changes: 106 additions & 0 deletions library-build-transformer/build.gradle
@@ -0,0 +1,106 @@
group 'io.realm'
version '1.0.0'

buildscript {
ext.kotlin_version = '1.2.51'

repositories {
mavenCentral()
jcenter()
}
dependencies {
classpath 'org.jfrog.buildinfo:build-info-extractor-gradle:4.5.2'
classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.7.3'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}

allprojects {
def props = new Properties()
props.load(new FileInputStream("${rootDir}/../realm.properties"))
props.each { key, val ->
project.ext.set(key, val)
}
}

group = 'io.realm'
version = file("${projectDir}/../version.txt").text.trim()

apply plugin: 'kotlin'
apply plugin: 'maven'
apply plugin: 'maven-publish'
apply plugin: 'com.jfrog.artifactory'
apply plugin: 'com.jfrog.bintray'

repositories {
google()
mavenCentral()
}

dependencies {
compile gradleApi()
compileOnly 'com.android.tools.build:gradle:3.1.1'
compile 'org.ow2.asm:asm:6.2'
compile 'org.ow2.asm:asm-util:6.2'
compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:${kotlin_version}"

testCompile group:'junit', name:'junit', version:'4.12'
testCompile "org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version"
testCompile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:${kotlin_version}"

}
compileKotlin {
kotlinOptions.jvmTarget = "1.8"
}
compileTestKotlin {
kotlinOptions.jvmTarget = "1.8"
}

def commonPom = {
licenses {
license {
name 'The Apache Software License, Version 2.0'
url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
distribution 'repo'
}
}
issueManagement {
system 'github'
url 'https://github.com/realm/realm-java/issues'
}
scm {
url 'scm:https://github.com/realm/realm-java'
connection 'scm:git@github.com:realm/realm-java.git'
developerConnection 'scm:git@github.com:realm/realm-java.git'
}
}

publishing {
publications {
realmPublication(MavenPublication) {
groupId 'io.realm'
artifactId = 'realm-library-build-transformer'
from components.java
pom.withXml {
Node root = asNode()
root.appendNode('name', 'realm-library-build-transformer')
root.appendNode('description', 'Transform library for Realm Java that will strip unwanted files at build time.')
root.appendNode('url', 'http://realm.io')
root.children().last() + commonPom
}
}
}
repositories {
maven {
credentials(AwsCredentials) {
accessKey project.hasProperty('s3AccessKey') ? s3AccessKey : 'noAccessKey'
secretKey project.hasProperty('s3SecretKey') ? s3SecretKey : 'noSecretKey'
}
if(project.version.endsWith('-SNAPSHOT')) {
url "s3://realm-ci-artifacts/maven/snapshots/"
} else {
url "s3://realm-ci-artifacts/maven/releases/"
}
}
}
}
1 change: 1 addition & 0 deletions library-build-transformer/gradle.properties
@@ -0,0 +1 @@
org.gradle.caching=true
Binary file not shown.
@@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.4.1-all.zip
172 changes: 172 additions & 0 deletions library-build-transformer/gradlew
@@ -0,0 +1,172 @@
#!/usr/bin/env sh

##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################

# 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
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null

APP_NAME="Gradle"
APP_BASE_NAME=`basename "$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=""

# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"

warn () {
echo "$*"
}

die () {
echo
echo "$*"
echo
exit 1
}

# 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
;;
esac

CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar

# Determine the Java command to use to start the JVM.
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"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
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
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, switch paths to Windows format before running java
if $cygwin ; 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=$((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" ;;
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, 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"

# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi

exec "$JAVACMD" "$@"

0 comments on commit 7d85693

Please sign in to comment.