Skip to content

Commit e82156d

Browse files
authored
Introduce bazel worker & drop support for bazel < 0.4.5 (#18)
* Adds experimental bazel worker support. * Introduce bazel worker & drop support for bazel < 0.4.5
1 parent 7589e84 commit e82156d

File tree

15 files changed

+843
-86
lines changed

15 files changed

+843
-86
lines changed

.travis.yml

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,6 @@ os:
1010
env:
1111
#- V=HEAD
1212
- V=0.4.5
13-
- V=0.4.4
14-
- V=0.4.3
15-
- V=0.4.2
16-
- V=0.4.1
17-
- V=0.4.0
18-
- V=0.3.2
1913

2014
before_install:
2115
- OS=linux

README.md

Lines changed: 17 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Kotlin Rules for Bazel
22
[![Build Status](https://travis-ci.org/pubref/rules_kotlin.svg?branch=master)](https://travis-ci.org/pubref/rules_kotlin)
33

4-
> Note: **These rules require Bazel 0.3.1 or higher**.
4+
> Note: **These rules require Bazel 0.4.5 or higher**.
55
66
These rules are for building [Kotlin][kotlin] source with with
77
[Bazel][bazel].
@@ -12,33 +12,30 @@ These rules are for building [Kotlin][kotlin] source with with
1212

1313
## Workspace rules
1414

15-
Add the following to your WORKSPACE file:
15+
Add the following to your `WORKSPACE` file:
1616

1717
```python
1818
git_repository(
1919
name = "org_pubref_rules_kotlin",
2020
remote = "https://github.com/pubref/rules_kotlin.git",
21-
tag = "v0.2.3", # update as needed
21+
tag = "v0.3.0", # update as needed
2222
)
23+
2324
load("@org_pubref_rules_kotlin//kotlin:rules.bzl", "kotlin_repositories")
25+
2426
kotlin_repositories()
2527
```
2628

2729
This will fetch a
28-
[release](https://github.com/JetBrains/kotlin/releases) (currently
29-
1.1.2-2) and expose the shell scripts and the runtime library.
30+
[kotlin release](https://github.com/JetBrains/kotlin/releases)
31+
(currently 1.1.2-2) and load a number of dependencies related to
32+
dagger (used to build the `KotlinCompiler` bazel worker).
3033

31-
```sh
32-
bazel query @com_github_jetbrains_kotlin//... --output label_kind
33-
# The kotlin runtime jar
34-
java_import rule @com_github_jetbrains_kotlin//:runtime
35-
# Script for the compiler
36-
sh_binary rule @com_github_jetbrains_kotlin//:kotlinc
37-
# Script for the runner
38-
sh_binary rule @com_github_jetbrains_kotlin//:kotlin
39-
```
34+
> You can override various dependencies loaded in the
35+
> `kotlin_repositories` rule via the `omit_*` options; see the source
36+
> file for details.
4037
41-
## Package (BUILD file) rules
38+
## BUILD rules
4239

4340
Add the following to your BUILD file:
4441

@@ -101,6 +98,7 @@ android_binary(
10198
| `jars` | `label_list` | List of jar file targets (`*.jar`) |
10299
| `x_opts` | `string_list` | List of additional `-X` options to `kotlinc` |
103100
| `plugin_opts` | `string_dict` | List of additional `-P` options to `kotlinc` |
101+
| `use_worker` | `boolean` | Assign to `False` to disable the use of [bazel workers](https://bazel.build/blog/2015/12/10/java-workers.html). |
104102

105103

106104
### kotlin_binary
@@ -149,20 +147,6 @@ available to other java rules via a `java_import` rule.
149147
In summary, you most likely do not need to interact with the
150148
`kotlin_compile` rule directly.
151149

152-
153-
## bazel.rc
154-
155-
With older versions of bazel, you may need to add the following line
156-
to your `tools/bazel.rc` file:
157-
158-
```
159-
build --strategy=KotlinCompile=standalone
160-
```
161-
162-
Alternatively, you can also add `--strategy=KotlinCompile=standalone`
163-
parameters to every `bazel run`, `bazel build`, etc. commands
164-
involving a kotlin rule.
165-
166150
# Summary
167151

168152
That's it! Hopefully these rules with make it easy to mix kotlin and
@@ -188,9 +172,10 @@ $ bazel run examples/helloworld:main_java
188172
## TODO
189173

190174
1. Implement a `kotlin_test` rule.
191-
2. Proper `data` and runfiles support.
192-
3. Research incremental compilation and bazel worker integration.
193-
4. kapt integration.
175+
1. Proper `data` and runfiles support.
176+
2. Android support.
177+
4. kapt support.
178+
3. Incremental compilation.
194179

195180
[bazel]: http://www.bazel.io
196181
[kotlin]: http://www.kotlinlang.org

WORKSPACE

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
load("//kotlin:java_import_external.bzl", "java_import_external")
12
load("//kotlin:rules.bzl", "kotlin_repositories")
23

34
kotlin_repositories()
@@ -7,12 +8,6 @@ maven_jar(
78
artifact = "junit:junit:jar:4.12",
89
)
910

10-
maven_jar(
11-
name = "io_reactivex_rxjava",
12-
artifact = "io.reactivex:rxjava:jar:1.2.1",
13-
#sha1 = "ssa",
14-
)
15-
1611
# Used to demonstrate/test maven dependencies
1712
maven_jar(
1813
name = "com_google_guava_guava_21_0",

java/io/bazel/rules/closure/BUILD

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# Copyright 2016 The Closure Rules Authors. All rights reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
licenses(["notice"]) # Apache 2.0
16+
17+
java_library(
18+
name = "BazelWorker",
19+
srcs = [
20+
"BazelWorker.java",
21+
],
22+
deps = [
23+
":worker_protocol_java_proto",
24+
"//java/io/bazel/rules/closure/program",
25+
"@com_google_dagger",
26+
"@com_google_guava",
27+
"@javax_inject",
28+
],
29+
visibility = ["//visibility:public"],
30+
)
31+
32+
java_proto_library(
33+
name = "worker_protocol_java_proto",
34+
deps = [
35+
":worker_protocol_java_proto_library",
36+
]
37+
)
38+
39+
proto_library(
40+
name = "worker_protocol_java_proto_library",
41+
srcs = ["worker_protocol.proto"],
42+
)
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
/*
2+
* Copyright 2016 The Closure Rules Authors. All rights reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.bazel.rules.closure;
18+
19+
import static java.nio.charset.StandardCharsets.UTF_8;
20+
21+
import com.google.common.base.CharMatcher;
22+
import com.google.common.base.Joiner;
23+
import com.google.common.base.Throwables;
24+
import com.google.common.collect.Iterables;
25+
import com.google.devtools.build.lib.worker.WorkerProtocol.WorkRequest;
26+
import com.google.devtools.build.lib.worker.WorkerProtocol.WorkResponse;
27+
import io.bazel.rules.closure.program.CommandLineProgram;
28+
import java.io.ByteArrayInputStream;
29+
import java.io.ByteArrayOutputStream;
30+
import java.io.IOException;
31+
import java.io.InputStream;
32+
import java.io.InterruptedIOException;
33+
import java.io.PrintStream;
34+
import java.lang.annotation.Documented;
35+
import java.lang.annotation.Retention;
36+
import java.lang.annotation.RetentionPolicy;
37+
import java.nio.file.Files;
38+
import java.nio.file.Path;
39+
import java.nio.file.Paths;
40+
import javax.inject.Inject;
41+
import javax.inject.Qualifier;
42+
43+
/**
44+
* Bazel worker runner.
45+
*
46+
* <p>This class adapts a traditional command line program so it can be spawned by Bazel as a
47+
* persistent worker process that handles multiple invocations per JVM. It will also be backwards
48+
* compatible with being run as a normal single-invocation command.
49+
*
50+
* @param <T> delegate program type
51+
*/
52+
public final class BazelWorker<T extends CommandLineProgram> implements CommandLineProgram {
53+
54+
/** Qualifier for name of Bazel persistent worker. */
55+
@Qualifier
56+
@Retention(RetentionPolicy.RUNTIME)
57+
@Documented
58+
public @interface Mnemonic {}
59+
60+
private final CommandLineProgram delegate;
61+
private final String mnemonic;
62+
private final PrintStream output;
63+
64+
@Inject
65+
public BazelWorker(T delegate, PrintStream output, @Mnemonic String mnemonic) {
66+
this.delegate = delegate;
67+
this.output = output;
68+
this.mnemonic = mnemonic;
69+
}
70+
71+
@Override
72+
public Integer apply(Iterable<String> args) {
73+
if (Iterables.contains(args, "--persistent_worker")) {
74+
return runAsPersistentWorker();
75+
} else {
76+
return delegate.apply(loadArguments(args, false));
77+
}
78+
}
79+
80+
private int runAsPersistentWorker() {
81+
InputStream realStdIn = System.in;
82+
PrintStream realStdOut = System.out;
83+
PrintStream realStdErr = System.err;
84+
try (InputStream emptyIn = new ByteArrayInputStream(new byte[0]);
85+
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
86+
PrintStream ps = new PrintStream(buffer)) {
87+
System.setIn(emptyIn);
88+
System.setOut(ps);
89+
System.setErr(ps);
90+
while (true) {
91+
WorkRequest request = WorkRequest.parseDelimitedFrom(realStdIn);
92+
if (request == null) {
93+
return 0;
94+
}
95+
int exitCode = 0;
96+
Iterable<String> args = loadArguments(request.getArgumentsList(), true);
97+
try {
98+
exitCode = delegate.apply(args);
99+
} catch (RuntimeException e) {
100+
if (wasInterrupted(e)) {
101+
return 0;
102+
}
103+
System.err.println(
104+
String.format("ERROR: Worker threw uncaught exception with args: %s",
105+
Joiner.on(' ').join(args)));
106+
e.printStackTrace(System.err);
107+
exitCode = 1;
108+
}
109+
WorkResponse.newBuilder()
110+
.setOutput(buffer.toString())
111+
.setExitCode(exitCode)
112+
.build()
113+
.writeDelimitedTo(realStdOut);
114+
realStdOut.flush();
115+
buffer.reset();
116+
System.gc(); // be a good little worker process and consume less memory when idle
117+
}
118+
} catch (IOException | RuntimeException e) {
119+
if (wasInterrupted(e)) {
120+
return 0;
121+
}
122+
Throwables.throwIfUnchecked(e);
123+
throw new RuntimeException(e);
124+
} finally {
125+
System.setIn(realStdIn);
126+
System.setOut(realStdOut);
127+
System.setErr(realStdErr);
128+
}
129+
}
130+
131+
private Iterable<String> loadArguments(Iterable<String> args, boolean isWorker) {
132+
String lastArg = Iterables.getLast(args, "");
133+
if (lastArg.startsWith("@")) {
134+
Path flagFile = Paths.get(CharMatcher.is('@').trimLeadingFrom(lastArg));
135+
if ((isWorker && lastArg.startsWith("@@")) || Files.exists(flagFile)) {
136+
if (!isWorker && !mnemonic.isEmpty()) {
137+
output.printf(
138+
"HINT: %s will compile faster if you run: "
139+
+ "echo \"build --strategy=%s=worker\" >>~/.bazelrc\n",
140+
mnemonic, mnemonic);
141+
}
142+
try {
143+
return Files.readAllLines(flagFile, UTF_8);
144+
} catch (IOException e) {
145+
throw new RuntimeException(e);
146+
}
147+
}
148+
}
149+
return args;
150+
}
151+
152+
private boolean wasInterrupted(Throwable e) {
153+
Throwable cause = Throwables.getRootCause(e);
154+
if (cause instanceof InterruptedException
155+
|| cause instanceof InterruptedIOException) {
156+
output.println("Terminating worker due to interrupt signal");
157+
return true;
158+
}
159+
return false;
160+
}
161+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Copyright 2016 The Closure Rules Authors. All rights reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
licenses(["notice"]) # Apache 2.0
16+
17+
java_library(
18+
name = "program",
19+
srcs = ["CommandLineProgram.java"],
20+
visibility = ["//visibility:public"],
21+
deps = ["@com_google_guava"],
22+
)
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* Copyright 2016 The Closure Rules Authors. All rights reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.bazel.rules.closure.program;
18+
19+
import com.google.common.base.Function;
20+
21+
/**
22+
* Interface for command line programs.
23+
*
24+
* <p>This is the same thing as a main function, except not static.
25+
*/
26+
public interface CommandLineProgram extends Function<Iterable<String>, Integer> {
27+
28+
/**
29+
* Runs blocking program start to finish.
30+
*
31+
* <p>This function might be called multiple times throughout the life of this object. Output
32+
* must be sent to {@link System#out} and {@link System#err}.
33+
*
34+
* @param args command line arguments
35+
* @return program exit code, i.e. 0 for success, non-zero for failure
36+
*/
37+
@Override
38+
Integer apply(Iterable<String> args);
39+
}

0 commit comments

Comments
 (0)