Skip to content

Commit

Permalink
Easy part of CompileStatic migration
Browse files Browse the repository at this point in the history
If groovy compiles without CompileStatic - code will be compiled in dynamic way,
e.g. All type checks will be in runtime. Erased types in bytecode.
With CompileStatic - compiler will save all typed in bytecode.
If all types saved, we can do static analysis of bytecode with java 8 capability.
More details can be found here: google#565 (comment)
  • Loading branch information
rougsig committed Jun 23, 2022
1 parent e7f9ab4 commit 45ef29f
Show file tree
Hide file tree
Showing 8 changed files with 62 additions and 46 deletions.
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ dependencies {
implementation 'commons-lang:commons-lang:2.6'

testImplementation 'junit:junit:4.12'
testImplementation "com.android.tools.build:gradle:3.5.0"
testImplementation('org.spockframework:spock-core:1.0-groovy-2.4') {
exclude module: 'groovy-all'
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,21 @@
*/
package com.google.protobuf.gradle;

import groovy.transform.CompileStatic;
import org.gradle.api.Project;
import org.gradle.api.file.FileTree;
import org.gradle.api.internal.file.FileOperations;

import javax.inject.Inject;

@CompileStatic
public interface ArchiveActionFacade {

FileTree zipTree(Object path);

FileTree tarTree(Object path);

@CompileStatic
class ProjectBased implements ArchiveActionFacade {

private final Project project;
Expand All @@ -59,6 +62,7 @@ public FileTree tarTree(Object path) {
}
}

@CompileStatic
abstract class ServiceBased implements ArchiveActionFacade {

// TODO Use public ArchiveOperations from Gradle 6.6 instead
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
*/
package com.google.protobuf.gradle;

import groovy.transform.CompileStatic;
import org.gradle.api.Action;
import org.gradle.api.Project;
import org.gradle.api.file.CopySpec;
Expand All @@ -42,9 +43,11 @@
* {@link org.gradle.api.file.FileSystemOperations} if available (Gradle 6.0+) or {@link org.gradle.api.Project#copy} if
* the version of Gradle is below 6.0.
*/
@CompileStatic
interface CopyActionFacade {
WorkResult copy(Action<? super CopySpec> var1);

@CompileStatic
class ProjectBased implements CopyActionFacade {
private final Project project;

Expand All @@ -58,6 +61,7 @@ public WorkResult copy(Action<? super CopySpec> action) {
}
}

@CompileStatic
abstract class FileSystemOperationsBased implements CopyActionFacade {
@Inject
public abstract FileSystemOperations getFileSystemOperations();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
*/
package com.google.protobuf.gradle

import groovy.transform.CompileDynamic
import groovy.transform.CompileStatic
import org.gradle.api.Named

/**
Expand All @@ -37,7 +37,7 @@ import org.gradle.api.Named
* configured, the plugin should try to run the executable from system search
* path.
*/
@CompileDynamic
@CompileStatic
class ExecutableLocator implements Named {

private final String name
Expand Down
50 changes: 26 additions & 24 deletions src/main/groovy/com/google/protobuf/gradle/GenerateProtoTask.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,6 @@
*/
package com.google.protobuf.gradle

import static java.nio.charset.StandardCharsets.US_ASCII

import com.google.common.base.Preconditions
import com.google.common.collect.ImmutableList
import groovy.transform.CompileDynamic
Expand Down Expand Up @@ -68,6 +66,8 @@ import org.gradle.util.ConfigureUtil
import javax.annotation.Nullable
import javax.inject.Inject

import static java.nio.charset.StandardCharsets.US_ASCII

/**
* The task that compiles proto files into Java files.
*/
Expand Down Expand Up @@ -98,9 +98,11 @@ public abstract class GenerateProtoTask extends DefaultTask {
// accidentally modifying them.
private String outputBaseDir
// Tags for selectors inside protobuf.generateProtoTasks; do not serialize with Gradle configuration caching
@SuppressWarnings("UnnecessaryTransientModifier") // It is not necessary for task to implement Serializable
@SuppressWarnings("UnnecessaryTransientModifier")
// It is not necessary for task to implement Serializable
transient private SourceSet sourceSet
@SuppressWarnings("UnnecessaryTransientModifier") // It is not necessary for task to implement Serializable
@SuppressWarnings("UnnecessaryTransientModifier")
// It is not necessary for task to implement Serializable
transient private Object variant
private ImmutableList<String> flavors
private String buildType
Expand Down Expand Up @@ -183,7 +185,7 @@ public abstract class GenerateProtoTask extends DefaultTask {
int baseCmdLength = baseCmd.sum { it.length() + CMD_ARGUMENT_EXTRA_LENGTH }
List<String> currentArgs = []
int currentArgsLength = 0
for (File proto: protoFiles) {
for (File proto : protoFiles) {
String protoFileName = proto
int currentFileLength = protoFileName.length() + CMD_ARGUMENT_EXTRA_LENGTH
// Check if appending the next proto string will overflow the cmd length limit
Expand Down Expand Up @@ -263,29 +265,29 @@ public abstract class GenerateProtoTask extends DefaultTask {
void setSourceSet(SourceSet sourceSet) {
checkInitializing()
Preconditions.checkState(!isAndroidProject.get(),
'sourceSet should not be set in an Android project')
'sourceSet should not be set in an Android project')
this.sourceSet = sourceSet
}

void setVariant(Object variant, boolean isTestVariant) {
checkInitializing()
Preconditions.checkState(isAndroidProject.get(),
'variant should not be set in a Java project')
'variant should not be set in a Java project')
this.variant = variant
this.isTestVariant = isTestVariant
}

void setFlavors(ImmutableList<String> flavors) {
checkInitializing()
Preconditions.checkState(isAndroidProject.get(),
'flavors should not be set in a Java project')
'flavors should not be set in a Java project')
this.flavors = flavors
}

void setBuildType(String buildType) {
checkInitializing()
Preconditions.checkState(isAndroidProject.get(),
'buildType should not be set in a Java project')
'buildType should not be set in a Java project')
this.buildType = buildType
}

Expand All @@ -297,7 +299,7 @@ public abstract class GenerateProtoTask extends DefaultTask {
@Internal("Inputs tracked in getSourceFiles()")
SourceSet getSourceSet() {
Preconditions.checkState(!isAndroidProject.get(),
'sourceSet should not be used in an Android project')
'sourceSet should not be used in an Android project')
Preconditions.checkNotNull(sourceSet, 'sourceSet is not set')
return sourceSet
}
Expand All @@ -319,7 +321,7 @@ public abstract class GenerateProtoTask extends DefaultTask {
@Internal("Not an actual input to the task, only used to find tasks belonging to a variant")
Object getVariant() {
Preconditions.checkState(isAndroidProject.get(),
'variant should not be used in a Java project')
'variant should not be used in a Java project')
Preconditions.checkNotNull(variant, 'variant is not set')
return variant
}
Expand Down Expand Up @@ -375,7 +377,7 @@ public abstract class GenerateProtoTask extends DefaultTask {
Provider<Map<String, String>> getReleaseDependenciesMapping() {
providerFactory.provider {
locatorToDependencyMapping.get()
.findAll { entry -> ! entry.value.endsWith ("-SNAPSHOT") }
.findAll { entry -> !entry.value.endsWith("-SNAPSHOT") }
}
}

Expand All @@ -402,26 +404,26 @@ public abstract class GenerateProtoTask extends DefaultTask {
@Internal("Not an actual input to the task, only used to find tasks belonging to a variant")
boolean getIsTestVariant() {
Preconditions.checkState(isAndroidProject.get(),
'isTestVariant should not be used in a Java project')
'isTestVariant should not be used in a Java project')
Preconditions.checkNotNull(variant, 'variant is not set')
return isTestVariant
}

@Internal("Not an actual input to the task, only used to find tasks belonging to a variant")
ImmutableList<String> getFlavors() {
Preconditions.checkState(isAndroidProject.get(),
'flavors should not be used in a Java project')
'flavors should not be used in a Java project')
Preconditions.checkNotNull(flavors, 'flavors is not set')
return flavors
}

@Internal("Not an actual input to the task, only used to find tasks belonging to a variant")
String getBuildType() {
Preconditions.checkState(isAndroidProject.get(),
'buildType should not be used in a Java project')
'buildType should not be used in a Java project')
Preconditions.checkState(
variant.name == 'test' || buildType,
'buildType is not set and task is not for local unit test variant')
variant.name == 'test' || buildType,
'buildType is not set and task is not for local unit test variant')
return buildType
}

Expand All @@ -439,7 +441,7 @@ public abstract class GenerateProtoTask extends DefaultTask {
String getDescriptorPath() {
if (!generateDescriptorSet) {
throw new IllegalStateException(
"requested descriptor path but descriptor generation is off")
"requested descriptor path but descriptor generation is off")
}
return descriptorSetOptions.path != null ? descriptorSetOptions.path : "${outputBaseDir}/descriptor_set.desc"
}
Expand Down Expand Up @@ -637,7 +639,7 @@ public abstract class GenerateProtoTask extends DefaultTask {
logger.debug "ProtobufCompile using files ${protoFiles}"

String protocPath = computeExecutablePath(protocLocator.get())
List<String> baseCmd = [ protocPath ]
List<String> baseCmd = [protocPath]
baseCmd.addAll(dirs)

// Handle code generation built-ins
Expand Down Expand Up @@ -720,7 +722,7 @@ public abstract class GenerateProtoTask extends DefaultTask {

private void checkCanConfig() {
Preconditions.checkState(state == State.CONFIG || state == State.INIT,
'Should not be called after configuration has finished')
'Should not be called after configuration has finished')
}

private void compileFiles(List<String> cmd) {
Expand Down Expand Up @@ -769,16 +771,16 @@ public abstract class GenerateProtoTask extends DefaultTask {
throw new GradleException(".jar protoc plugin path '${jarAbsolutePath}' has no file name")
}
File scriptExecutableFile = new File("${projectLayout.buildDirectory.get()}/scripts/" +
jarFileName[0..(jarFileName.length() - JAR_SUFFIX.length() - 1)] + "-${getName()}-trampoline." +
(isWindows ? "bat" : "sh"))
jarFileName[0..(jarFileName.length() - JAR_SUFFIX.length() - 1)] + "-${getName()}-trampoline." +
(isWindows ? "bat" : "sh"))
try {
mkdirsForFile(scriptExecutableFile)
String javaExe = computeJavaExePath(isWindows)
// Rewrite the trampoline file unconditionally (even if it already exists) in case the dependency or versioning
// changes we don't need to detect the delta (and the file content is cheap to re-generate).
String trampoline = isWindows ?
"@ECHO OFF\r\n\"${escapePathWindows(javaExe)}\" -jar \"${escapePathWindows(jarAbsolutePath)}\" %*\r\n" :
"#!/bin/sh\nexec '${escapePathUnix(javaExe)}' -jar '${escapePathUnix(jarAbsolutePath)}' \"\$@\"\n"
"@ECHO OFF\r\n\"${escapePathWindows(javaExe)}\" -jar \"${escapePathWindows(jarAbsolutePath)}\" %*\r\n" :
"#!/bin/sh\nexec '${escapePathUnix(javaExe)}' -jar '${escapePathUnix(jarAbsolutePath)}' \"\$@\"\n"
scriptExecutableFile.write(trampoline, US_ASCII.name())
setExecutableOrFail(scriptExecutableFile)
logger.info("Resolved artifact jar: ${jarAbsolutePath}. Created trampoline file: ${scriptExecutableFile}")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,15 @@
*/
package com.google.protobuf.gradle

import groovy.transform.CompileDynamic
import groovy.transform.CompileStatic
import org.gradle.api.Project
import org.gradle.api.internal.file.FileResolver
import org.gradle.util.ConfigureUtil

/**
* Adds the protobuf {} block as a property of the project.
*/
@CompileDynamic
@CompileStatic
class ProtobufConvention {
ProtobufConvention(Project project, FileResolver fileResolver) {
protobuf = new ProtobufConfigurator(project, fileResolver)
Expand Down
37 changes: 21 additions & 16 deletions src/main/groovy/com/google/protobuf/gradle/ToolsLocator.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@
*/
package com.google.protobuf.gradle

import groovy.transform.CompileDynamic
import com.google.gradle.osdetector.OsDetector
import groovy.transform.CompileStatic
import org.gradle.api.NamedDomainObjectContainer
import org.gradle.api.Project
import org.gradle.api.artifacts.Configuration
Expand All @@ -38,12 +39,12 @@ import org.gradle.api.file.FileCollection
/**
* Holds locations of all external executables, i.e., protoc and plugins.
*/
@CompileDynamic
@CompileStatic
class ToolsLocator {

private final Project project
private final ExecutableLocator protoc
private final NamedDomainObjectContainer<ExecutableLocator> plugins
final ExecutableLocator protoc
final NamedDomainObjectContainer<ExecutableLocator> plugins

static List<String> artifactParts(String artifactCoordinate) {
String artifact
Expand All @@ -53,11 +54,14 @@ class ToolsLocator {
String version
String classifier

(artifact, extension) = artifactCoordinate.tokenize('@')
List<String> artifactCoordinateTokenized = artifactCoordinate.tokenize('@')
(artifact, extension) = [artifactCoordinateTokenized[0], artifactCoordinateTokenized[1]]
if (extension == null && artifactCoordinate.endsWith('@')) {
extension = ''
}
(group, name, version, classifier) = artifact.tokenize(':')
List<String> artifactTokenized = artifact.tokenize(':')
(group, name, version, classifier) =
[artifactTokenized[0], artifactTokenized[1], artifactTokenized[2], artifactTokenized[3]]

return [group, name, version, classifier, extension]
}
Expand Down Expand Up @@ -92,19 +96,20 @@ class ToolsLocator {

void registerDependencyWithTasks(ExecutableLocator locator, Collection<GenerateProtoTask> protoTasks) {
// create a project configuration dependency for the artifact
Configuration config = project.configurations.create("protobufToolsLocator_${locator.name}") {
visible = false
transitive = false
extendsFrom = []
Configuration config = project.configurations.create("protobufToolsLocator_${locator.name}") { Configuration conf ->
conf.visible = false
conf.transitive = false
}
String groupId, artifact, version, classifier, extension
(groupId, artifact, version, classifier, extension) = artifactParts(locator.artifact)
OsDetector osdetector = project.extensions.getByName("osdetector") as OsDetector
List<String> parts = artifactParts(locator.artifact)
(groupId, artifact, version, classifier, extension) = [parts[0], parts[1], parts[2], parts[3], parts[4]]
Map<String, String> notation = [
group:groupId,
name:artifact,
version:version,
classifier:classifier ?: project.osdetector.classifier,
ext:extension ?: 'exe',
group:groupId,
name:artifact,
version:version,
classifier:classifier ?: osdetector.classifier,
ext:extension ?: 'exe',
]
Dependency dep = project.dependencies.add(config.name, notation)
FileCollection artifactFiles = config.fileCollection(dep)
Expand Down
4 changes: 2 additions & 2 deletions src/main/groovy/com/google/protobuf/gradle/Utils.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
package com.google.protobuf.gradle

import com.google.common.base.Preconditions
import groovy.transform.CompileDynamic
import groovy.transform.CompileStatic
import org.apache.commons.lang.StringUtils
import org.gradle.api.GradleException
import org.gradle.api.Project
Expand All @@ -43,7 +43,7 @@ import java.util.regex.Matcher
/**
* Utility classes.
*/
@CompileDynamic
@CompileStatic
class Utils {
/**
* Returns the conventional name of a configuration for a sourceSet
Expand Down

0 comments on commit 45ef29f

Please sign in to comment.