4 ms·
I really recommend getting comfortable with make -- if you know just enough to be dangerous (PHONY targets, regular targets, ifeq, ?=), it can be an incredibly
by hardwaresofton 6y ago
I really recommend getting comfortable with make -- if you know just enough to be dangerous (PHONY targets, regular targets, ifeq, ?=), it can be an incredibly useful tool, and unify control/operations across projects.
The complexity/required reading that comes with integrating ~5+ individual tools and libraries you have to be aware of across one language can be made a lot easier by using make to do the plumbing between these tools.
Often I find that I need to do some of the following:
- do just a tiny bit of pre-processing
- use two disparate tools which might interact/have an ordering requirement
- alias a simple command for a repetitive task
And it's the case that:
- The tools/libraries I'm using don't have the functionality built in
- I'm not excited about writing a bash script
- A <programming language> script/build-hook feels too heavy
I find that Makefiles are a really good way to boil down and standardize my builds across projects.
Here's a chunk from a somewhat recent project:
psql: export DB_CONTAINER_NAME=$(shell $(KUBECTL) get pods -n $(K8S_NAMESPACE) -l component=db -o=name | head -n 1)
psql:
$(KUBECTL) exec -it $(DB_CONTAINER_NAME) -n $(K8S_NAMESPACE) psql -- --user $(DB_USER)
And another that's a bit less trendy:
image: check-tool-docker
$(DOCKER) build \
-f infra/docker/Dockerfile \
-t ${IMAGE_FULL_NAME_SHA} \
.
image-publish: check-tool-docker
$(DOCKER) push ${IMAGE_FULL_NAME_SHA}
image-release:
$(DOCKER) tag $(IMAGE_FULL_NAME_SHA) $(IMAGE_FULL_NAME)
$(DOCKER) push $(IMAGE_FULL_NAME)
And earlier in the file is the actual language-specific stuff:
lint: check-tool-yarn
$(YARN) lint
Yeah, all I'm doing is just calling through to yarn here but the nice thing is that `lint` is a pretty common target from project to project, so most places `make lint` will do what I think it is going to do.
And if you're wondering what the check-tool-<x> targets are like:
check-tool-yarn:
ifeq (,$(shell which $(YARN)))
$(error "yarn not installed (see https://yarnpkg.com)")
endif
I will warn that there are people with Makefile PTSD, but I am finding a lot of value from it these days, would encourage people to take a look. If you're really ready for some fun check out recursive make -- managing and orchestrating subprojects is really really easy (and you don't have to care what the sub project is written in at the top level).
- KajMagnus 6y agoHow do you pass arguments to your Make targets? Let's say you'd like to set `DB_USER` to "my_name", then, do you: make psql user=my_name # ? To me, having to type `user=` is very annoying, I want to do just `make psql my_name`. Agreed that Make is nice :- ) My Makefile is not as nice as Make though o.O > I'm not excited about writing a bash script Bash scripts almost could be illegal :- ) I'm thinking about writing scripts in Deno in the future, instead
- hardwaresofton 6y agoLots of ways! So this is one of those things that's subtly quite interesting -- Make will both take all variables from the environment, and make them Make variables. So this command: $ USER=my_name make psql The above example is the creation of a temporary ENV variable (assuming your shell supports this feature, it's equivalent to doing an export but only for that line), and the USER variable (accessed by using $(USER) or ${USER} in your makefile somewhere) will be automatically populated. That is distinct from the following: $ make psql USER=my_name What you've done there is actually make-specific, you're giving make a Make variable, so Make will know about $(USER)/${USER}, and it will also show up, because Make exposes it's variables to shells (each line is it's own shell) that is run[0]. So this Makefile: .PHONY: my-tgt my-tgt: echo -e "double dollar to print a bash var => $$MYVAR" Produces the same thing -- $$MYVAR is actually a way to use a shell variable (the $ has to be escaped), but you get the same result putting USER= before and after: $ MYVAR=from-the-shell make my-tgt double dollar to print a bash var => [from-the-shell] $ make my-tgt MYVAR=from-make double dollar to print a bash var => [from-make] All that said, if you don't need to ever change the name then just make the target `psql-<username>` or `psql_username`, problem solved! For example, I use kubie on my kubernetes cluster, and I have explicitly named clusters, so when I want to enter a kubie context (just so I can avoid some typing), I run `make <cluster name>-kubie` and I'm off -- I didn't feel the need to do `make kubie CLUSTER=<cluster name>` though I easily could have. Here's what it actually looks like: mycluster-kubie: CLUSTER=mycluster $(KUBIE) ctx -f secrets/ansible/the.dns.name.of.my.server/var/lib/k0s/pki/admin.conf I don't strictly need that CLUSTER=mycluster there, but if the command cared, then it could be easily "hard coded". > Bash scripts almost could be illegal :- ) I'm thinking about writing scripts in Deno in the future, instead Bash is super powerful, and some people are good at it, but it just hasn't stuck enough for me to be comfortable. Plus, it still feels like another language to be wrangled while Make's only purpose is to do the things (whatever that thing is) that build your software. [EDIT] figured I might share some more nice pieces of software that I use and love (also so you don't think I'm just willy nilly checking in secrets to my repository): - direnv[1] for managing local ENV settings in a given folder (like your project folder) - git-crypt[2] for simple encryption of secrets in a git repository, with gpg support - entr[3] it re-runs a given command when files change, really good for reducing feedback loops when working with tooling that doesn't have --watch/fsnotify functionality [0]: https://www.gnu.org/software/make/manual/html_node/Environment.html#Environment https://www.gnu.org/software/make/manual/html_node/Environme... [1]: https://direnv.net/docs/hook.html https://direnv.net/docs/hook.html [2]: https://www.agwa.name/projects/git-crypt/ https://www.agwa.name/projects/git-crypt/ [3]: https://eradman.com/entrproject/ https://eradman.com/entrproject/