Dude
Dude

Reputation: 271

Making a Java Makefile

EDIT: Essentially, I am trying to do the same thing as typing "javac sim.java" and then "java sim (commands)". Is there a way to replace this using a makefile?

So I have looked at a lot of examples online and tried to modify them to what I need, but nothing has worked. I need a makefile so that my teacher can create my project just by typing "make" into terminal (this is a requirement of the project for some reason). My end goal is to be able to type "sim" followed by the required commands as defined by my code. The code runs in eclipse, but I can't get it to run using these commands in terminal. It will make the file, but it says "sim: command not found" when I try to type "sim....(arguments)" into terminal. I'm sure this is a dumb question but we have not learned about this in school and I have no experience with Makefile.

Below is my makefile.

JFLAGS = -g
JC = javac
OPT = -O3
#OPT = -g
WARN = -Wall

sim: sim.class

sim.class: sim.java
    $(JC) $(JFLAGS) sim.java

clean:
    $(RM) sim.class

clobber:
    $(RM) sim.class

Upvotes: 11

Views: 59526

Answers (5)

Hackerman
Hackerman

Reputation: 141

This answer aims to be more developed than the ones provided.

I want to reiterate the aforementioned comment that .class files cannot always be built independently. this means that we need to build the application completely initially, and only after that we will be able to update a single file every time its source changes. Also, if more than one file changes it is very likely that the entire application will have to be rebuilt.

This is the file tree am working with.

enter image description here

Now that we have this in mind, lets look at the make file.

JV = java
JC = javac

# Find java files
JAVA_FILES := $(shell find -name *.java)
# Now use JAVA_FILES to make CLASS_FILES (with whole paths)
CLASS_FILES :=  $(patsubst ./src/main/java/%.java,build/%.class,$(JAVA_FILES))

BUILD_DIR = ./build
SRC_DIR = ./src/main/java

# Make the build directory
DIRS = $(BUILD_DIR)

# Remove files
CLEAN = $(SRCS) $(BUILD_DIR)

# After the Application is first built, use this
# to update single files. NOTE: Might not work
# if multiple files are updated.
# When A new file class is created, you
# will have to clean and build the project again
update:  $(BUILD_DIR) $(CLASS_FILES)
.PHONY =  update Main clean

# This is the main executable you want
TARGET = mypack.Main

# Alias for that executable, you can use -> make Main
# to build it.
Main: $(TARGET)

# You can have more executables
# TARGET2 = someotherpack.OtherMain

# Rule to build .class files
$(BUILD_DIR)/%.class: $(SRC_DIR)/%.java | $(BUILD_DIR)
    @echo "[$JC]: $@  << $<"
    @$(JC) -cp $(BUILD_DIR) -d $(BUILD_DIR) $<

# Rule to build main executable
$(TARGET): $(BUILD_DIR)
    @echo "[JV]: " $@
    $(JV) -cp build $(TARGET)

# Make the build directory
$(BUILD_DIR):
    @ if ! [ -e $(BUILD_DIR) ]; then mkdir $(BUILD_DIR); fi
    @echo "[JC]: " $@
    @$(JC) $(JAVA_FILES) -d $(BUILD_DIR)

# Remove files
clean:
    @$(foreach file, $(CLEAN), if [ -e "$(file)" ]; then echo "[RM] $(file)"; rm -r "$(file)";fi;)


The first time make is ran with

make Main

The output will be as follows

[JC]:  build
[JV]:  mypack.Main
java -cp build mypack.Main
This is the main class
This is a do object
This another another
Last one

The extra output are print statements from the source code. Then running

make 

will produce

make: Nothing to be done for 'update'.

And asumming Last.java is updated, running it again it will show

[C]: build/mypack/mysubpack/Last.class  << src/main/java/mypack/mysubpack/Last.java

Note that there are two packages here,

mypack
mypack.mysubpack

Make sure that your source files are in the right packages

Upvotes: 0

Aron Hoogeveen
Aron Hoogeveen

Reputation: 522

If you have a simple directory layout like the following,

root/
    src/
        ExampleClass.java
        AnotherClass.java
    out/
makefile

you could create your makefile like this:

##
# source directory
##
SRC_DIR := src

##
# output directory
##
OUT_DIR := out

##
# sources
##
SRCS := $(wildcard $(SRC_DIR)/*.java)

##
# classes
## 
CLS := $(SRCS:$(SRC_DIR)/%.java=$(OUT_DIR)/%.class)

##
# compiler and compiler flags
##
JC := javac
JCFLAGS := -d $(OUT_DIR)/ -cp $(SRC_DIR)/

##
# suffixes
##
.SUFFIXES: .java

##
# targets that do not produce output files
##
.PHONY: all clean

##
# default target(s)
##
all: $(CLS)

$(CLS): $(OUT_DIR)/%.class: $(SRC_DIR)/%.java
    $(JC) $(JCFLAGS) $<

##
# clean up any output files
##
clean:
    rm $(OUT_DIR)/*.class

Upvotes: 7

Jamie McLaughlin
Jamie McLaughlin

Reputation: 572

make executes a recipe by executing its dependencies, then the recipe itself. Therefore if you add a command which runs your compiled code to the top recipe, typing 'make' will also run the code after it is compiled. I cannot believe how many people do not know this!

Something like:

sim: sim.class
    java sim

Will do what your tutor requires.

Upvotes: 3

bmargulies
bmargulies

Reputation: 100196

Using make with java can be an exercise in driving screws with a hammer as soon as you have more than one class.

Working java code will use packages. So you'll have a complex directory tree that mirrors your package structure, with your .java source files in it. Writing make rules that 'see' all those files is a pain.

You should invoke javac on all your source files at the same time to get correct results. So, the usual make pattern of 'run one command to turn one source file into one compiled file' does not work so well.

However, it seems as if your main problem at the moment is that you expect java to produce an executable; a file with a name like 'sim'. Nope, not going to happen in any simple way. The java compiler produces .class files; a whole tree of them for your source files. You can run directly from them, or you can package them up in a JAR file.

To get all the way to something that appears to be a simple command line executable, you need to use a more complex tool that wraps all this up with a script on the front.

For now, you just need to do:

java -cp . NAME_OF_THE_CLASS_IN_sim.java

to run after your makefile completes.

Upvotes: 7

Jeff Miller
Jeff Miller

Reputation: 1444

For a simple project without many files or dependencies, I simply use scripts.

To build:

javac -cp .;* *.java

To run:

java -cp .;* SomeMainClass

Replace . with whatever path(s) you need for your source. The * will use any jar on the default path or use a different path like lib/*.

Upvotes: 7

Related Questions