Skip to content

Commit 9a3714b

Browse files
committed
Packaging, Compilation, and Just
1 parent ba8dde6 commit 9a3714b

31 files changed

+805
-10
lines changed

src/SUMMARY.md

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -841,7 +841,7 @@ Make them do one. -->
841841
- [java.base](./modules/java.base.md)
842842
- [The Unnamed Module](./modules/the_unnamed_module.md)
843843
- [Module Imports](./modules/module_imports.md)
844-
- [Multi-Module Directory Layout]()
844+
- [Multi-Module Directory Layout](./modules/multi_module_directory_layout.md)
845845
- [Lambdas](./lambdas.md)
846846
- [Functional Interfaces](./lambdas/functional_interfaces.md)
847847
- [@FunctionalInterface](./lambdas/functional_interface_annotation.md)
@@ -861,20 +861,46 @@ Make them do one. -->
861861
# Sharing Code
862862

863863
- [Compilation](./compilation.md)
864-
- [javac]()
865-
- [--release]()
866-
- [Packaging]()
867-
- [jar]()
868-
- [--main-class]()
869-
- [Documentation]()
870-
- [javadoc]()
864+
- [javac](./compilation/javac.md)
865+
- [Class Files](./compilation/class_files.md)
866+
- [Modules](./compilation/modules.md)
867+
- [Compile Multiple Files](./compilation/multiple_files.md)
868+
- [Clean](./compilation/clean.md)
869+
- [-g](./compilation/g.md)
870+
- [Running Compiled Code](./compilation/running_compiled_code.md)
871+
- [Challenges](./compilation/challenges.md)
872+
- [Packaging](./packaging.md)
873+
- [jar](./packaging/jar.md)
874+
- [Jar Files](./packaging/jar_files.md)
875+
- [--module-path](./packaging/module_path.md)
876+
- [--main-class](./packaging/main_class.md)
877+
- [Libraries](./packaging/libraries.md)
878+
- [Challenges](./packaging/challenges.md)
879+
880+
# Tools
881+
882+
- [just](./just.md)
883+
- [Installation](./just/installation.md)
884+
- [Justfile](./just/justfile.md)
885+
- [Recipes](./just/recipes.md)
886+
- [Dependencies](./just/dependencies.md)
887+
- [Documentation Comments](./just/documentation_comments.md)
888+
- [Further Reading](./just/further_reading.md)
889+
- [Challenges](./just/challenges.md)
890+
891+
# Sharing Code II
892+
893+
- [Distribution](./distribution.md)
894+
- [jars](./distribution/jars.md)
895+
- [jlink]()
896+
- [Documentation](./documentation.md)
871897
- [Documentation Comments](./documentation/documentation_comments.md)
898+
- [javadoc]()
872899
- [@param]()
873900
- [@return]()
874901
- [@throws]()
875902
- [Markdown]()
876-
- [Distribution]()
877-
- [jlink]()
903+
878904

879905
<!---
880906
PROJECT IDEAS:

src/compilation.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,13 @@
33
The first step to sharing code you've written with other people is to
44
compile it.
55

6+
Compilation in the context of programming languages means that a program
7+
reads all your code, ensures it abides by the rules of the language,
8+
and translates your code to some other form. Usually this other form
9+
is in some manner more directly "runnable" than the code you started with.
10+
11+
We call programs that do this reading, ensuring, and translation "compilers."
12+
13+
This happens behind the scenes when you run `java src/Main.java`
14+
but for sharing code you will need to do it yourself as a separate
15+
step.

src/compilation/challenges.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Challenges
2+
3+
Remember the rules for this are
4+
5+
- Try to use only the information given up to this point in this book.
6+
- Try not to give up until you've given it a solid attempt
7+
8+
## Challenge 1.
9+
10+
Take your code from one of the previous projects and compile all of it
11+
to class files.
12+
13+
Make sure you can then run it using those class files.
14+
15+
## Challenge 2.
16+
17+
Take the code for one of the previous projects and put it in
18+
the multi-module directory layout.
19+
20+
Compile it using the `--module-source-path` and `--module`
21+
options instead of listing the files out explicitly.
22+
23+
Make sure you can then run it using the `--module-path` and `--add-modules ALL-MODULE-PATH`
24+
flags.
25+
26+
27+
## Challenge 3.
28+
29+
Run the command from the previous command again, this time making sure to pass `-g`.
30+
Be sure to also clean whatever directory you used for output.

src/compilation/class_files.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Class Files
2+
3+
The format that the Java compiler outputs is called a "class file."
4+
5+
These class files are what is loaded into the "Java Virtual Machine" to actually run your program. This is
6+
in part because the Java Virtual Machine was conceived as a general tool not technicially specific to Java.
7+
8+
There are other languages besides Java - such as [Clojure](https://clojure.org/) and [Kotlin](https://kotlinlang.org/) - which
9+
also can be compiled into class files.
10+
11+
```text
12+
13+
Java Source Code --> javac -------------\
14+
----> Class Files --> Java Virtual Machine
15+
Kotlin Source Code --> kotlinc ---------/ |
16+
/
17+
Lombok Source Code --> lombokc ----------/
18+
19+
... and more
20+
```
21+
22+
This lets those languages make use of the Java Virtual Machine and all the millions
23+
of dollars and decades of work put into making it run code fast.[^bringup]
24+
25+
[^bringup]: I bring this up because if you are only thinking about Java it might seem like a
26+
pointless extra step. It somewhat is, but at the same time it lets other languages "compete"
27+
on more or less even ground. The JVM is a labor of fill-in-the-blank, that is for sure.

src/compilation/clean.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Clean
2+
3+
If you compile code multiple times you should "clean"
4+
your working directory before each compilation.
5+
6+
This is because when you run `javac -d output ...` the compiler
7+
will simply dump class files into the destination folder.
8+
It will not remove any "stale" class files from earlier compiler runs.
9+
10+
This can lead you to be in a state where you think your code is working
11+
but if you ever compiled it again, from scratch, you would get errors.
12+
13+
The simplest way to handle this in bash is to use the `rm` tool. `rm` **r**e**m**oves
14+
files. Before you run the compiler clear out any old output with `rm -rf output`.
15+
`-rf` stands for "**r**ecursive" and "**f**orce." This is what you need to delete whole
16+
folders.[^simple]
17+
18+
[^simple]: This technique - just deleting folders and doing the work from scratch again - can be slow.
19+
The upside of this approach is that it is simple to understand.
20+
It also probably doesn't matter since your computer is fast. When you need to really efficiently compile
21+
huge codebases you turn to a special kind of program called a build tool. These are complex beasts but can
22+
avoid unneccesary work.

src/compilation/g.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# -g
2+
3+
There are a lot of options you can pass to `javac` to alter its behavior.
4+
One of the ones that I find useful is `-g`. The `g` stands for "**g**enerate debug info."[^obvious]
5+
6+
This makes sure the generated class files have information about things like what variables were named
7+
what. This, in turn, helps Java give better error messages
8+
9+
For example, if you have a program like the following:
10+
11+
```java
12+
class Main {
13+
void main() {
14+
String nocturne = null;
15+
IO.println(nocturne.length());
16+
}
17+
}
18+
```
19+
20+
While in all situations it will ultimately crash with a `NullPointerException`, if
21+
you did not compile with `-g` you might get a stack trace like the following.
22+
You will probably need to scroll to the right to see the relevant bit.
23+
24+
```text,no_run
25+
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "String.length()" because "<local1>" is null
26+
at Main.main(Main.java:4)
27+
```
28+
29+
Whereas using `-g` will get you an exception that includes the variable name of what was `null`.
30+
31+
```text,no_run
32+
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "String.length()" because "nocturne" is null
33+
at Main.main(Main.java:4)
34+
```
35+
36+
So, with rare exceptions, you should always use `-g`.
37+
38+
39+
[^obvious]: Obviously.

src/compilation/javac.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# javac
2+
3+
The compiler used for compiling Java code is called `javac`.
4+
This stands for "**Java** **C**ompiler".
5+
6+
Its job is to take a list of `.java` files and compile
7+
them into `.class` files.
8+
9+
For a single file Java program you can do this by running
10+
a command similar to the following.
11+
12+
```bash
13+
javac -d output src/Main.java
14+
```
15+
16+
The `-d output` in that example means "put the compiled class files in a folder called `output`."
17+
After running the command above you would expect to see something like the following.
18+
19+
```text
20+
src/
21+
Main.java
22+
output/
23+
Main.class
24+
```
25+
26+
You aren't guarenteed that any given `.java` file will produce only one `.class` file
27+
as output. Inner classes are one reason for this, but there are others.
28+
29+
```text
30+
src/
31+
Main.java
32+
output/
33+
Main.class
34+
Main$1.class
35+
Main$Thing.class
36+
```
37+
38+
To compile multiple files you need to list every `.java` file
39+
one after another.
40+
41+
```

src/compilation/modules.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Modules
2+
3+
For reasons that will become apparent as we proceed,
4+
it is best if all all the code you intend to share
5+
is contained within a named module.
6+
7+
This means at minimum your classes would need to be in packages
8+
and would need to provide a `module-info.java`.
9+
10+
Many things will work even without that but some important ones
11+
will not.

src/compilation/multiple_files.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# Compile Multiple Files
2+
3+
To have `javac` compile multiple files you have a few options.
4+
5+
The first is to simply list every file you want to compile one after
6+
the other when calling `javac`.
7+
8+
```bash
9+
javac -d output src/Main.java src/Other.java src/Another.java
10+
```
11+
12+
This has the obvious downside of needing you to add new files to what can
13+
become a large list over time.
14+
15+
If you get far enough in learning bash you can paper over this
16+
with commonly available tools like `find`.
17+
18+
```bash
19+
javac -d output $(find ./src -name "*.java" -type f)
20+
```
21+
22+
Where the `$()` is bash syntax that runs the command in the parentheses and uses its
23+
output as arguments to the command being run. `find` is a tool that lists all files that
24+
match some criteria. In this case all files (as opposed to folders) that have a name that
25+
ends with `.java`.
26+
27+
The other option is to structure your project using the multi-module directory layout.
28+
29+
```
30+
your.project/
31+
src/
32+
code/
33+
Main.java
34+
Other.java
35+
module-info.java
36+
```
37+
38+
If you do this then you can compile all the code in a module by using the `--module-source-path` and `--module` options.
39+
40+
```bash
41+
javac -d output --module-source-path "./*/src" --module your.project
42+
```
43+
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# Running Compiled Code
2+
3+
The way you ultimately run your compiled code depends on
4+
whether or not all your code is in packages.
5+
6+
If you have any classes in the unnamed package - which
7+
will only be the case when said code is also not in a named
8+
module - you should run your code like so:
9+
10+
```bash,no_run
11+
java --class-path output Main
12+
```
13+
14+
Where you can substitute `Main` for whatever the class you want to run is.
15+
So if you want to run a class named `Impromptu` in the `chopin` package
16+
you would run `java --class-path output chopin.Impromptu`.
17+
18+
`--class-path` should be self-explanatory. It is the path where `java`
19+
will look for class files.
20+
21+
But if you do not have any classes in the unnamed package - which will
22+
be hopefully be the case when you share code with others[^conflicts] -
23+
you instead want to run your code like this.
24+
25+
```
26+
java \
27+
--module-path output \
28+
--add-modules ALL-MODULE-PATH
29+
composers.Main
30+
```
31+
32+
The `--module-path` option is very similar to the `--class-path` option. The difference
33+
is that all the code on the `--module-path` will be loaded with more strict rules.[^before]
34+
35+
The `--add-modules ALL-MODULE-PATH` bit just means "load all the code on the module path."[^auth]
36+
37+
[^conflicts]: Remember the social convention of reverse domain name notation (`com.google`, etc)
38+
39+
[^before]: You can think of `--class-path` as sort of a "compatibility mode" for libraries
40+
and code written before Java had a concept of modules. The difference isn't super important
41+
other than the very specific case of wanting to compile and run classes in the unnamed package.
42+
Well that and "troublesome" libraries, but we will get to that later.
43+
44+
[^auth]: Chances are Java will make this the default in the future. For now you must write it though.

0 commit comments

Comments
 (0)