Skip to content
Permalink
Browse files

Initial support for windows development (#977)

Motivation:
ServiceTalk currently doesn't build on windows. This can be limiting for
folks who are comfortable with that development environment.

Modifications:
- grpc-protoc project needs refactoring of the plugin in order to
execute on unix and windows platforms. The distribution is now
consistent with a script as the executable and co-located uber jar
containing the plugin logic.
- ignore/modify tests that failed when running locally on windows, there
will be followup PRs for other tests that sporadically fail.

Result:
ServiceTalk can now be built on windows.
  • Loading branch information
Scottmitch committed Mar 24, 2020
1 parent 3bd64a9 commit 0ebe48ed3948f22bfc003c8a0c551572bb70bbbf
Showing with 328 additions and 56 deletions.
  1. +1 −0 buildSrc/build.gradle
  2. +73 −0 buildSrc/src/main/java/io/servicetalk/internal/build/ExecutableBuilder.java
  3. +103 −0 gradlew.bat
  4. +2 −2 ...rrent-api/src/test/java/io/servicetalk/concurrent/api/completable/CompletableToPublisherTest.java
  5. +1 −1 servicetalk-data-jackson-jersey/gradle/checkstyle/suppressions.xml
  6. +3 −2 servicetalk-examples/grpc/helloworld/build.gradle
  7. +3 −2 servicetalk-examples/grpc/routeguide/build.gradle
  8. +2 −1 ...-gradle-plugin-internal/src/main/groovy/io/servicetalk/gradle/plugin/internal/ProjectUtils.groovy
  9. +5 −1 ...lugin-internal/src/main/groovy/io/servicetalk/gradle/plugin/internal/ServiceTalkCorePlugin.groovy
  10. +11 −4 ...grpc-gradle-plugin/src/main/groovy/io/servicetalk/grpc/gradle/plugin/ServiceTalkGrpcPlugin.groovy
  11. +3 −2 servicetalk-grpc-netty/build.gradle
  12. +2 −1 servicetalk-grpc-netty/gradle/checkstyle/suppressions.xml
  13. +57 −16 servicetalk-grpc-protoc/build.gradle
  14. +2 −2 servicetalk-grpc-protoc/gradle/checkstyle/suppressions.xml
  15. +5 −4 servicetalk-http-api/gradle/checkstyle/suppressions.xml
  16. +5 −4 servicetalk-http-api/src/test/java/io/servicetalk/http/api/AbstractHttpRequestMetaDataTest.java
  17. +5 −4 servicetalk-http-api/src/test/java/io/servicetalk/http/api/AbstractHttpResponseMetaDataTest.java
  18. +2 −2 servicetalk-http-netty/gradle/checkstyle/suppressions.xml
  19. +2 −0 ...icetalk-http-netty/src/test/java/io/servicetalk/http/netty/H2PriorKnowledgeFeatureParityTest.java
  20. +7 −0 servicetalk-http-netty/src/test/java/io/servicetalk/http/netty/HttpRequestEncoderTest.java
  21. +2 −0 servicetalk-http-netty/src/test/java/io/servicetalk/http/netty/HttpServerMultipleRequestsTest.java
  22. +2 −0 ...etalk-http-netty/src/test/java/io/servicetalk/http/netty/InsufficientlySizedExecutorHttpTest.java
  23. +2 −0 servicetalk-http-netty/src/test/java/io/servicetalk/http/netty/MultiAddressUrlHttpClientTest.java
  24. +3 −0 ...cetalk-http-netty/src/test/java/io/servicetalk/http/netty/NettyHttpServerConnectionDrainTest.java
  25. +8 −0 servicetalk-http-netty/src/test/java/io/servicetalk/http/netty/NettyHttpServerTest.java
  26. +2 −1 servicetalk-http-router-jersey/gradle/checkstyle/suppressions.xml
  27. +2 −2 servicetalk-http-utils/gradle/checkstyle/suppressions.xml
  28. +1 −2 servicetalk-test-resources/src/test/java/io/servicetalk/test/resources/DefaultTestCertsTest.java
  29. +2 −1 servicetalk-transport-netty-internal/gradle/checkstyle/suppressions.xml
  30. +9 −1 ...ternal/src/test/java/io/servicetalk/transport/netty/internal/RequestResponseCloseHandlerTest.java
  31. +1 −1 servicetalk-utils-internal/gradle/checkstyle/suppressions.xml
@@ -27,6 +27,7 @@ if (!repositories) {
groovyClass.getDeclaredMethod("inheritRepositoriesFromBuildscript", Project).invoke(null, project)
}

apply plugin: "java"
apply plugin: "java-gradle-plugin"
apply from: "../servicetalk-grpc-gradle-plugin/plugin-config.gradle"
apply from: "../servicetalk-gradle-plugin-internal/plugin-config.gradle"
@@ -0,0 +1,73 @@
/*
* Copyright © 2020 Apple Inc. and the ServiceTalk project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.servicetalk.internal.build;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;

public final class ExecutableBuilder {

private ExecutableBuilder() {
// no instances
}

public static void buildUnixExecutable(String uberJarName, File outputFile) throws IOException {
prepareOutputFile(outputFile);
try(FileOutputStream execOutputStream = new FileOutputStream(outputFile)) {
execOutputStream.write(("#!/bin/sh\n" +
"pushd $(dirname \"$0\") > /dev/null\n" +
"exec java -jar " + uberJarName + " \"$@\"\n" +
"popd > /dev/null\n").getBytes(StandardCharsets.US_ASCII));
}
finalizeOutputFile(outputFile);
}

public static void buildWindowsExecutable(String uberJarName, File outputFile) throws IOException {
prepareOutputFile(outputFile);
try(FileOutputStream execOutputStream = new FileOutputStream(outputFile)) {
execOutputStream.write(("@ECHO OFF\r\n" +
"pushd %~dp0\r\n" +
"java -jar " + uberJarName + " %*\r\n" +
"popd\r\n").getBytes(StandardCharsets.US_ASCII));
}
finalizeOutputFile(outputFile);
}

public static String addExecutablePostFix(String rawName, boolean isWindows) {
return rawName + (isWindows ? ".bat" : ".sh");
}

private static void prepareOutputFile(File outputFile) throws IOException {
if (!outputFile.exists()) {
if (!outputFile.getParentFile().isDirectory() && !outputFile.getParentFile().mkdirs()) {
throw new IOException("unable to make directories for file: " + outputFile.getCanonicalPath());
}
} else {
// Clear the file's contents
new PrintWriter(outputFile).close();
}
}

private static void finalizeOutputFile(File outputFile) throws IOException {
if (!outputFile.setExecutable(true)) {
outputFile.delete();
throw new IOException("unable to set file as executable: " + outputFile.getCanonicalPath());
}
}
}
@@ -0,0 +1,103 @@
@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 init

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 init

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

:init
@rem Get command-line arguments, handling Windows variants

if not "%OS%" == "Windows_NT" goto win9xME_args

:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2

:win9xME_args_slurp
if "x%~1" == "x" goto execute

set CMD_LINE_ARGS=%*

: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 %CMD_LINE_ARGS%

: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
@@ -165,8 +165,8 @@ private CountDownLatch publishOnOriginalIsPreserved0(final ConcurrentLinkedQueue
"(from Publisher). Thread: " + currentThread()));
}
})
.beforeOnComplete(analyzed::countDown)
.beforeOnError(__ -> analyzed.countDown())
.afterOnComplete(analyzed::countDown)
.afterOnError(__ -> analyzed.countDown())
.afterOnSubscribe(__ -> receivedOnSubscribe.countDown())
)
.subscribe(subscriber);
@@ -21,5 +21,5 @@
<suppressions>
<!-- CheckStyle wrongly thinks a Junit TestSuite is a utility class -->
<suppress checks="HideUtilityClassConstructor"
files="io/servicetalk/data/jackson/jersey/JerseyDataJacksonTestSuite.java"/>
files="io[\\/]servicetalk[\\/]data[\\/]jackson[\\/]jersey[\\/]JerseyDataJacksonTestSuite.java"/>
</suppressions>
@@ -33,6 +33,7 @@ serviceTalkGrpc {

// The following setting must be omitted in users projects and is necessary here
// only because we want to use the locally built version of the plugin
serviceTalkProtocPluginPath =
"${project.rootProject.rootDir}/servicetalk-grpc-protoc/build/buildExecutable/protoc-gen-servicetalk_grpc.exe"
serviceTalkProtocPluginPath = "${project.rootProject.rootDir}/servicetalk-grpc-protoc/build/buildExecutable/" +
io.servicetalk.internal.build.ExecutableBuilder.addExecutablePostFix("protoc-gen-servicetalk_grpc",
org.gradle.internal.os.OperatingSystem.current().isWindows())
}
@@ -35,6 +35,7 @@ serviceTalkGrpc {

// The following setting must be omitted in users projects and is necessary here
// only because we want to use the locally built version of the plugin
serviceTalkProtocPluginPath =
"${project.rootProject.rootDir}/servicetalk-grpc-protoc/build/buildExecutable/protoc-gen-servicetalk_grpc.exe"
serviceTalkProtocPluginPath = "${project.rootProject.rootDir}/servicetalk-grpc-protoc/build/buildExecutable/" +
io.servicetalk.internal.build.ExecutableBuilder.addExecutablePostFix("protoc-gen-servicetalk_grpc",
org.gradle.internal.os.OperatingSystem.current().isWindows())
}
@@ -85,7 +85,8 @@ final class ProjectUtils {
def fenc = System.getProperty("file.encoding")
if (!"UTF-8".equalsIgnoreCase(fenc)) {
throw new GradleException("File encoding must be UTF-8 but is $fenc, consider using a file system that " +
"supports it or setting the JAVA_TOOL_OPTIONS env var to: -Dfile.encoding=UTF-8");
"supports it or setting the JAVA_TOOL_OPTIONS env var to: -Dfile.encoding=UTF-8.\n\nMake sure the jvm is " +
"restarted to pickup these changes (e.g. `gradle --stop`, and restart your IDE)!");
}
}

@@ -126,6 +126,11 @@ class ServiceTalkCorePlugin implements Plugin<Project> {
idea.workspace.iws.withXml { XmlProvider provider ->
appendNodes(provider, getClass().getResourceAsStream("idea/iws-components.xml"))
}
// idea plugin doesn't account for buildSrc directory, so manually add it.
idea.module.iml.withXml { XmlProvider provider ->
Node contentNode = provider.asNode().component.find { it.@name == "NewModuleRootManager" }.content[0]
contentNode.appendNode("sourceFolder", [url: "file://\$MODULE_DIR\$/buildSrc/src/main/java"])
}
}
}
}
@@ -179,7 +184,6 @@ class ServiceTalkCorePlugin implements Plugin<Project> {
}
}
}

}
}
}
@@ -21,11 +21,9 @@ import org.gradle.api.InvalidUserDataException
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.tasks.compile.JavaCompile
import org.gradle.plugins.ide.eclipse.EclipsePlugin
import org.gradle.plugins.ide.idea.IdeaPlugin
import org.gradle.plugins.ide.idea.model.IdeaModel
import org.gradle.tooling.model.idea.IdeaModule
import org.gradle.util.GradleVersion

class ServiceTalkGrpcPlugin implements Plugin<Project> {
@@ -42,6 +40,15 @@ class ServiceTalkGrpcPlugin implements Plugin<Project> {
ServiceTalkGrpcExtension extension = project.extensions.create("serviceTalkGrpc", ServiceTalkGrpcExtension)
extension.conventionMapping.generatedCodeDir = { project.file("$project.buildDir/generated/source/proto") }

def compileOnlyDeps = project.getConfigurations().getByName("compileOnly")
project.beforeEvaluate {
def serviceTalkProtocPluginPath = extension.serviceTalkProtocPluginPath
if (!serviceTalkProtocPluginPath) {
compileOnlyDeps.add(
project.getDependencies().create("io.servicetalk:servicetalk-grpc-protoc:$serviceTalkVersion:all"))
}
}

project.afterEvaluate {
Properties pluginProperties = new Properties()
pluginProperties.load(getClass().getResourceAsStream("/META-INF/servicetalk-grpc-gradle-plugin.properties"))
@@ -60,7 +67,6 @@ class ServiceTalkGrpcPlugin implements Plugin<Project> {
throw new InvalidUserDataException("Please set `serviceTalkGrpc.protobufVersion`.")
}


project.configure(project) {
Task ideaTask = extension.generateIdeConfiguration ? project.tasks.findByName("ideaModule") : null
Task eclipseTask = extension.generateIdeConfiguration ? project.tasks.findByName("eclipse") : null
@@ -75,7 +81,8 @@ class ServiceTalkGrpcPlugin implements Plugin<Project> {
if (serviceTalkProtocPluginPath) {
path = file(serviceTalkProtocPluginPath)
} else {
artifact = "io.servicetalk:servicetalk-grpc-protoc:$serviceTalkVersion"
artifact = "io.servicetalk:servicetalk-grpc-protoc:$serviceTalkVersion@" +
(org.gradle.internal.os.OperatingSystem.current().isWindows() ? "bat" : "sh")
}
}
}
@@ -54,8 +54,9 @@ serviceTalkGrpc {

// The following setting must be omitted in users projects and is necessary here
// only because we want to use the locally built version of the plugin
serviceTalkProtocPluginPath =
"${project.rootProject.rootDir}/servicetalk-grpc-protoc/build/buildExecutable/protoc-gen-servicetalk_grpc.exe"
serviceTalkProtocPluginPath = "${project.rootProject.rootDir}/servicetalk-grpc-protoc/build/buildExecutable/" +
io.servicetalk.internal.build.ExecutableBuilder.addExecutablePostFix("protoc-gen-servicetalk_grpc",
org.gradle.internal.os.OperatingSystem.current().isWindows())
}

afterEvaluate {
@@ -19,5 +19,6 @@
"https://checkstyle.org/dtds/suppressions_1_2.dtd">

<suppressions>
<suppress checks="VisibilityModifier" files="io/servicetalk/grpc/netty/ProtocolCompatibilityTest.java"/>
<suppress checks="VisibilityModifier"
files="io[\\/]servicetalk[\\/]grpc[\\/]netty[\\/]ProtocolCompatibilityTest.java"/>
</suppressions>

0 comments on commit 0ebe48e

Please sign in to comment.
You can’t perform that action at this time.