Skip to content

Repobuild java tutorial

Christopher Van Arsdale edited this page Oct 21, 2013 · 2 revisions

Overview

This will help you create a new "repobuild" based project. This tutorial is in Java.

Step 1: Install Repobuild

If you have not already, install repobuild (see README)

Step 2: Creating a new github project
  1. Fork the example_cpp project: https://github.com/chrisvana/repobuild_java_example
  2. Set up the repository locally:
$ git clone https://github.com/<username>/repobuild_java_example
$ cd repobuild_java_example
Step 3: Create the Checker Binary

Currently, the BUILD file looks like this:

// Empty build file
[]

We are going to create a new java binary (jar) called "checker"

[
{ "java_binary": {
    "name": "checker",
    "java_sources": [ "Checker.java" ]
} }
]

And now create Checker.java:

// Simple checker main.

public class Checker {
  public static void main(String[] args) {
    System.out.println("Are you classified as human?");
  }
}

Now run repobuild:

$ repobuild ":checker"
Processing: //:checker
Processing: //:checker.0
Processing: //:checker
Generating: Makefile

And build it:

$ make
Compiling:  checker (java)
Jaring:     .gen-obj/checker.jar

And test it:

$ ./checker
Are you classified as human?

At this point, your client should look like this.

Step 4: Adding a library

Update our build file to add "parser":

[
{ "java_library": {
    "name": "parser",
    "java_sources": [ "Parser.java" ]
} },
{ "java_binary": {
    "name": "checker",
    "java_sources": [ "Checker.java" ],
    "dependencies": [ ":parser" ]
} }
]

Now add Parser.java:

// Example library

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public class Parser {
  public void Parse(InputStream input, OutputStream output) throws IOException {
    // For now, do nothing but mirror the input.
    int character ;
    while ((character = input.read()) >= 0) {
      output.write(character);
    }
  }
}

Great, now run repobuild:

$ repobuild ":checker"
Processing: //:checker
Processing: //:parser
Processing: //:checker.0
Processing: //:checker
Generating: Makefile

And run make:

$ make
Compiling:  parser (java)
Compiling:  checker (java)
Jaring:     .gen-obj/checker.jar

Now let's make sure this works:

$ echo 'Negative, I am a meat popsicle' | ./checker
Negative, I am a meat popsicle

Wonderful. Your client should now look like this.

Step 5: Protocol Buffers

Add a proto library to your codebase:

[
{ "proto_library": {
    "name": "my_proto",
    "sources": [ "my_proto.proto" ],
    "generate_java": true
} },
{ "java_library": {
    "name": "parser",
    "java_sources": [ "Parser.java" ],
    "dependencies": [ ":my_proto" ]
} },
{ "java_binary": {
    "name": "checker",
    "java_sources": [ "Checker.java" ],
    "dependencies": [ ":parser" ]
} }
]

proto_library (by default) uses //third_party/protobuf:... as its source. Let's add //third_party:

$ git submodule add https://github.com/chrisvana/third_party.git
Cloning into 'third_party'...
remote: Counting objects: 58, done.
remote: Compressing objects: 100% (50/50), done.
remote: Total 58 (delta 26), reused 39 (delta 7)
Unpacking objects: 100% (58/58), done.
Checking connectivity... done

Now re-run repobuild (note it auto-fetches required sub-modules from //third_party):

$ repobuild ":checker"
Processing: //:checker
Processing: //:parser
Processing: //:checker.0
Processing: //:my_proto
Processing: //:checker
Processing: //:my_proto.0
Processing: //:my_proto.1
Processing: //third_party/protobuf:java_proto
remote: Counting objects: 744, done.
remote: Compressing objects: 100% (459/459), done.
remote: Total 744 (delta 267), reused 738 (delta 261)
Receiving objects: 100% (744/744), 2.49 MiB | 540.00 KiB/s, done.
Resolving deltas: 100% (267/267), done.
Processing: //third_party/protobuf:generate_java_descriptor_proto
Processing: //third_party/protobuf:auto_.0
Processing: //third_party:auto_.0
Processing: //third_party/protobuf:protoc
Processing: //third_party/protobuf:protoc_lib
Processing: //third_party/protobuf:proto_lib
Processing: //third_party/protobuf:protoc.0
Generating: Makefile

Now run make:

$ make clean
$ make -j8
Compiling:  third_party/protobuf/src/google/protobuf/descriptor.pb.cc (c++)
Compiling:  third_party/protobuf/src/google/protobuf/descriptor.cc (c++)
Compiling:  third_party/protobuf/src/google/protobuf/descriptor_database.cc (c++)
Compiling:  third_party/protobuf/src/google/protobuf/dynamic_message.cc (c++)
Compiling:  third_party/protobuf/src/google/protobuf/extension_set.cc (c++)
Compiling:  third_party/protobuf/src/google/protobuf/extension_set_heavy.cc (c++)
Compiling:  third_party/protobuf/src/google/protobuf/generated_message_reflection.cc (c++)
Compiling:  third_party/protobuf/src/google/protobuf/generated_message_util.cc (c++)
... <cut> ...
Linking:    .gen-obj/third_party/protobuf/protoc
Script:     //third_party/protobuf:generate_java_descriptor_proto
Compiling:  third_party/protobuf/java_proto (java)
Protobuf:   //:my_proto.0
Compiling:  my_proto.1 (java)
Compiling:  parser (java)
Compiling:  checker (java)
Jaring:     .gen-obj/checker.jar

Your client should look like this.

Step 6: Making a proto syntax checker

Tying everything together, update Parser.java to check if the input is a valid instance of our protocol buffer:

// Example library

import com.google.protobuf.TextFormat;
import com.google.protobuf.TextFormat.ParseException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintStream;

public class Parser {
  public void Parse(InputStream input, OutputStream output) throws IOException {
    MyProto.FooProto.Builder builder = MyProto.FooProto.newBuilder();
    PrintStream stream = new PrintStream(output);
    try {
      TextFormat.merge(new InputStreamReader(input, "ASCII"), builder);
      stream.println("Success!");
    } catch (ParseException e) {
      stream.println("Failed to Parse ASCII input: " + e);
    }
  }
}

Run make (dependencies were added in the previous step, so no need to re-run repobuild):

$ make
Compiling:  parser (java)
Compiling:  checker (java)
Jaring:     .gen-obj/checker.jar

Now test it:

# Ok protobuf:
$ echo "int_val: 10 bool_val: false" | ./checker
Success!

# Unknown field:
$ echo "int_val: 10 bool_val: false unknown_val: 123" | ./checker
Failed to Parse ASCII input: com.google.protobuf.TextFormat$ParseException: 1:29: Message type "FooProto" has no field named "unknown_val".

# Random garbage:
$ echo 'asdf' | ./checker
Failed to Parse ASCII input: com.google.protobuf.TextFormat$ParseException: 1:1: Message type "FooProto" has no field named "asdf".

Great, now your client should look something like this.

Conclusion

Congratulations, you've built a jar that depends a library, and some third_party open source code. Check out some other examples: https://github.com/chrisvana/repobuild/wiki/Examples