Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

use Makefile for all tasks #17

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

merlinran
Copy link
Collaborator

It is based off #12 and the change specific to this PR is in a single commit 541629a

@sequoia-hope
Copy link
Contributor

sequoia-hope commented Jan 4, 2022

Make has always seemed pretty hard to follow for me. Are there any alternatives you can think of?

Is it just executing some commands? I am curious if we could achieve much the same results with a python script?

could call it acorn-run.py and mark the execute bit, so instead of calling
make simulation
we could call
acorn-run.py simulation

we could alias it to the command "arun" so we just type:

arun simulatiuon

Or is make really needed? Let me know your thoughts. Thank you!

@merlinran
Copy link
Collaborator Author

Some sort of build automation seems useful to, for example, rebuild the docker image automatically before running simulation if any of the requirements.txt gets changed, or some other guy updating to the latest code would find it fail to run because of missing Python packages, and spend some minutes to figure out why and do the docker build by themselves. We could do the check of timestamp and rebuilding in Unix shell, but that's less structural than a tool dedicated to this, and IMHO shell is messier and harder to debug than Makefile (the errors you encountered when running make in the other PR are due to differences among shell flavours). If we use Python, there would be tons of os.system/subprocess.popen to run the commands and get the output, which seems even more suboptimal.

I chose make because of its ubiquity - and many developers are more or less familiar with it. An alternative would be https://www.scons.org which is Python-like, but that is yet another domain specific language for most of the people to learn. Same for https://github.com/bazelbuild/bazel and others.

From my experience, Makefile seldom changes once gets established, and though the syntax has many dark corners I myself would frustrate too, the basic rules are simple. Actually the majority of the Makefile are plain shell scripts. I'll comment on the Makefile features. Lmk if they make sense to you and if not, we can discuss on the alternatives.

Copy link
Collaborator Author

@merlinran merlinran left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is basically it. I never dedicated time to learn make, just followed the Makefiles I had to make changes and checked the manual for things I didn't quite understand.

@@ -0,0 +1,64 @@
LOCAL_IMAGE = acorn_docker:1.0
REMOTE_IMAGE = merlinran/acorn_docker
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Variables are similar to shell variables, you just need to reference them via $(var), instead of $${var}(${var} in normal shell, but since we are running shell scripts within Makefile, we need to escape $, similar to why we have to use \\ to represent \ in C string).

LOCAL_IMAGE = acorn_docker:1.0
REMOTE_IMAGE = merlinran/acorn_docker

.PHONY: list
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

. PHONY is to tell Make that the target is not a real file. Without it, if there's a file called list in the directory, make would think: the file is already there and up-to-date, I won't do anything.

REMOTE_IMAGE = merlinran/acorn_docker

.PHONY: list
list:
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is the first target in the file, make without a target simply runs this rule. And this rule has no dependencies.


.PHONY: list
list:
@echo "Welcome! You may want to try one of the following:\n---"; \
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ tells make to not prompt the command follows. The rest are just normal shell scripts. This rule is a trick to list the main targets from the Makefile itself.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One caveat is to always use tab instead of whitespaces in the rule body, or make won't recognize it. Editor may warn you or correct for you if that happens.

grep "^.PHONY:" Makefile | grep "#" | cut -d ":" -f 2- | sed "s/\w*#/\t#/g" | sed "s/^/make/"

.PHONY: simulation # Run the vehicle and server containers in simulation mode.
simulation: docker-image
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here docker-image is a dependency of simulation. Before running the body of the rule, make first makes sure docker-image is up-to-date, by recursively checking the rule at L59 and running the body if needed.
In this case, the dependency chain is simulation <- docker-image <- Dockerfile <- vehicle/requirements.txt and server/requirements.txt. As long as the upstream target is newer than the downstream target, the rule body will be run. It would be very difficult to express such relationship in plain shell or Python.


.PHONY: attach-vehicle # Attach to the shell of the vehicle container. It creates the container if it doesn't exist.
attach-vehicle:
@test -z `docker ps -f "name=acorn_vehicle" -q` && make docker-vehicle; \
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here's again plain shell. Start the container if it doesn't exist, then docker exec it. test -z returns 0(success) if the output of the command is empty, in which case make docker-vehicle will run; Otherwise it returns 1(fail), and the && short-circuits. The command after ; is not affected, hence being run unconditionally.

.PHONY: push-image # Build and push image to Docker Hub to be used by CI.
push-image: docker-image
ifeq ($(shell uname -m),arm64)
docker buildx build -t $(REMOTE_IMAGE) --platform linux/amd64,linux/arm64 --push .
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is to allow me building and pushing the image for linux/amd64 while on my MacBook with M1 processor. Otherwise it just hits the next branch.


.PHONY: test # Run tests on Linux (if the Python dependencies are installed) or inside Docker. Otherwise, you probably want to try `make docker-test` instead.
test:
coverage run -m pytest --log-cli-level DEBUG && coverage report --skip-covered --skip-empty
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is supposed to be run inside the container, but since you are on Linux, you can also make test directly if all required Python packages are installed.

@sequoia-hope sequoia-hope force-pushed the main branch 2 times, most recently from ac03432 to e8ef5d5 Compare February 8, 2024 09:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants