- Running Example Programs
- Hello World with JLang
- Building Larger Projects With JLang
- Building Shared Libraries with JLang
Note: JLang is not yet tested or built for use on Windows.
Clone the repo and build using the “Quick start guide” section of the README.
There are a fair number of dependencies you’ll need to install before building JLang:
- ANT - Polyglot and JLang are built with ant
- JDK native libraries - Requires a manual download from Oracle’s website. (Select Java SE Development Kit 7u80 for your target platform).
- LLVM + Clang 5.0 - Used to compile the runtime C++ and make executables from *.ll files.
- Boehm-Demers-Weiser garbase collector - This implements garbage collection for LLVM and must be linked with the generated code.
- Git LFS - used to download the JDK7 java source which is distributed with this repo.
More details on how to install all of these dependencies are included in the README.
Below is a list of the environment variables used by JLang (an
N/a default means it is not optional):
|JDK7||N/a||The absolute path pointing to your JDK7 installation (e.g. /usr/lib/jvm/jdk1.7.0_80/)|
|JDK||jdk||Which set of JDK java libraries to compile. For small unit tests that don’t require the full openjdk, use
|CLANG_VERSION||If you have multiple versions of clang/llvm installed, this can be used to select them. CLANG_VERSION=5.0 means that we will look for the executables
To build the JLang compiler and runtime, simply type
make from the top-level directory!
$ /home/user/JLang> printf "$JDK7\n" /usr/lib/jvm/jdk1.7.0_80/ $ /home/user/JLang> printf "$JDK\n" jdk $ /home/user/JLang> printf "$CLANG_VERSION\n" 5.0 $ /home/user/JLang> make --- Checking setup --- + JDK 7 found + git lfs installed + LLVM version is up to date: 5.0.0 Setup looks good --- Building Polyglot --- ... --- Building compiler --- ... --- Building jdk classes --- ... --- Building runtime --- ... --- Building jdk --- ... $ /home/user/JLang>
Before running the build, your configuration will be checked for correctness.
If you get an error message, be sure the appropriate dependency is installed (and on your shell’s
PATH) and your environment variables are correctly configured.
Running Example Programs
Compiling the Test Suite
You can run the
make tests command from the top-level directory to run our unit test suite, which will report all failed unit tests as either expected or regressions.
You should see an output similar to the following
$ /home/user/JLang> make tests ... ... Generating output for Unary Generating output for UnaryOperations Generating output for VarArgs ******** CHECKING CORRECTNESS ********* ============== No regressions found! ============== ============== ============== The following expected failures occurred: FieldReflection MethodReflection ThreadingTest $ /home/user/JLang>
Compiling the CUP Parser Generator Example
examples/cup directory we have included a distribution of the source code for the CUP parser generator.
You can build CUP from source with JLang using the
make cup command from the top-level directory.
$ /home/user/JLang> make cup ... --- Building the CUP Parser Generator --- Compiling Java file(s) down to LLVM IR Creating CUP executable Compiling Java runtime to class files Note: Some input files use unchecked or unsafe operations. Note: Recompile with -Xlint:unchecked for details. Compiling LLVM IR to Objects for Lib Creation Compiling out/java_cup/runtime/virtual_parse_stack.ll Compiling out/java_cup/runtime/lr_parser.ll Compiling out/java_cup/runtime/ComplexSymbolFactory.ll Compiling out/java_cup/runtime/Scanner.ll Compiling out/java_cup/runtime/SymbolFactory.ll Compiling out/java_cup/runtime/Symbol.ll Compiling out/java_cup/runtime/DefaultSymbolFactory.ll Compiling Cup Java runtime files to libcup $ /home/user/JLang>
This will build both a CUP executable (accessible via the
examples/cup/bin/cup.sh script) and a CUP runtime library which can be used by JLang generated code.
Once you’ve compiled CUP you can try out any of the example languages supplied with the distribution in
$ /home/user/JLang> cd examples/cup/test $ /home/user/JLang/examples/cup/test> ../bin/cup.sh example.cup ------- CUP v0.11b 20160615 (GIT 3d0ae71) Parser Generation Summary ------- 0 errors and 0 warnings 10 terminals, 2 non-terminals, and 9 productions declared, producing 19 unique parse states. 0 terminals declared but not used. 0 non-terminals declared but not used. 0 productions never reduced. 0 conflicts detected (0 expected). Code written to "parser.java", and "sym.java". ---------------------------------------------------- (CUP v0.11b 20160615 (GIT 3d0ae71)) $ /home/user/JLang/examples/cup/test> ls example.cup parser.java sym.java $ /home/user/JLang/examples/cup/test>
Hello World with JLang
Compiling Hello World.java
Once you’ve built JLang and the JDK runtime using
make in the top-level directory you can test out JLang with a sample program.
Create a simple
HelloWorld.java file, printing to stdout using
System.out.println(). Compile using
$ /home/user/JLang> ./bin/jlangc -cp "$JDK"/out/classes HelloWorld.java
This will generate a file called
HelloWorld.ll, which contains ‘human-readable’ LLVM IR.
To compile a
*.ll file to an executable, it will need to be linked with
- The compiled OpenJDK java sources (via libjdk)
- The compiled JLang JVM (via libjvm)
- The OpenJDK native code found in the OpenJDK shared libraries.
We have provided a script for compiling with the appropriate linking flags in
$ /home/user/JLang> ./bin/compile_ll.sh Hello.ll Wrote compiled binary to Hello.o $ /home/user/JLang>
Running Hello World.o
Once clang++ generates an output binary, your binary only needs one more runtime configuration. The
JAVA_HOME environment variable must point to the
JDK7 directory. If you currently have Java installed on your machine and don’t want to overwrite your system’s
JAVA_HOME variable, you can execute the binary like so:
$ /home/user/JLang> JAVA_HOME=$JDK7 bash -c './HelloWorld.o'
We have provided a simple wrapper script which will do this for you in
$ /home/user/JLang> ./bin/execute.sh Hello.o hello world! $ /home/user/JLang>
Building Larger Projects With JLang
In general, building larger projects which are distributed over more source files requires a slightly different build process.
Compiling *.java files
JLang requires the user to specify a single entry point since that will become the final top-level LLVM module and executable.
If only 1 source file is provided, such as in the HelloWorld example, then that java file’s
static main method will be used as the entry point.
In the case where multiple source files are present, you can use the
--entry-point option to specify the entry class by full class name. The
-sourcepath option can be used to search for any other *.java files required to compile the main class. If
--entry-point is not specified, then JLang will not search for other *.java files.
For example, the following command could be used to compile a large number of java classes into a single application:
$ /home/user/JLang/example_app> ../bin/jlangc -cp ../"$JDK"/out/classes -sourcepath src -d out --entry-point org.startup.app.Main src/org/startup/app/Main.java
In this command,
-cp ../"$JDK"/out/classesspecifies all of the class files which the compiler may assume already exist as a JLang library. In this case, we are just speciyfying the JDK on our classpath.
-sourcepath srcoption says to start looking for *.java files under the
-d outspecifies that the output *.ll files should be placed in the
--entry-pointsays that the file in
src/org/startup/app/Main.javais the entry point (as per java package & path naming convention)
Compiling *.ll files
compile_ll.sh script provided in the
bin directory can still be used to compile many *.ll files into a single executable.
As long as your classpath is not dependent upon any pre-built libraries other than the JDK, you can simply run this script with all of the *.ll files produced by JLang as the arguments.
From our example above:
$ /home/user/JLang/example_app> find out -name "*.ll" | xargs ../bin/compile_ll.sh AppExec Wrote compiled binary to AppExec.o $ /home/user/JLang/example_app>
The compilation script’s first argument is the name of the executable to produce and all other arguments are the list of *.ll files to compile. It expects exactly 1 of these files to be a “top-level” module (which should be
out/org/startup/app/Main.ll in our example).
Executing this app should be exactly the same as the HelloWorld case! Nothing changes between single file and large project compilations at this phase.
$ /home/user/JLang/example_app> ../bin/execute.sh AppExec.o arg1 arg2 -o1 optarg... ... PROFIT $ /home/user/JLang/example_app>
Building Shared Libraries with JLang
Documentation coming soon
(TLDR; compile each *.ll file into a *.o file individually and then use the
-shared flag for clang to export them as a single shared library)