Live data from Hacker News

Using Makefile(s) for Go

danishpraka.sh

21–30 of 104 posts

Re: Using Makefile(s) for Go

#21
post #8
post #3

I've seen a lot of developers, especially developers with C backgrounds, reach for Makefiles when approaching Go development, and I think it's a harmful practice. So, I'd like to offer a respectful but firm rebuttal to this article. :) I dislike using make(1) with Go for two reasons. The first is that make was developed for building C projects, and therefore is oriented around that task. Building C projects is a lot…

>A Go project should only require the Go compiler to build successfully. But they don't. The go compiler doesn't support yarn, npm, protobuf, open-api generators, doc generators like md2man, go-bindata-assetfs, gox and everything you need to complete the code generation done in modern Go applications. So how do you orchestrate this? People use Makefiles, bash scripts, go scripts and everything in between and combined…

>It's there and has been used for decades for a reason.

I have always had problems to comply with such statements when the reasons aren't given.

Re: Using Makefile(s) for Go

#22
I use Makefiles for Go projects all the time, but not in the way the article describes. First off, in a pre `go mod` world, if you had dependencies to check before running the build, then a Makefile was the easiest way to manage that. But even in a post `go mod` world, there are good reasons to use one that the article totally overlooks:

* Makefiles introduce a topological sort to build steps. This is the reason you use it instead of build shell scripts: it allows build steps to run in parallel, it guarantees order by dependency which is the best way to read build steps, and it makes file freshness an easy element to check for a build step, which is still needed for Go projects with multiple subpackages.

* Go projects usually have more than go files that are required in making an executable. If you run a web server and you are bundling static pages into your executable, Makefiles are the best way to handle that.

* If you are building for multiple architectures, or want to encode the git tag/branch into the executable, it is better to have that Makefile bake in the necessary options on the build step and keep it uniform across the build.

* If you write a Go file and bake that into a Docker image, I find it best to drop the image and container hashes into files so that I can get to them easily for docker exec/attach/rm/rmi commmands.

But there is one bigger reason Makefiles work for our entire team. We standardize on using Makefiles as the entrypoint for our builds. We have a polyglot environment at work so sometimes it gets confusing to figure out how to build a project. By standardizing on running 'make' we are all on the same page. Have a Javascript project to webpack? Run make and have make call yarn. Have a python wheel to construct? Run make and have make call python setup.py. You have a Java project that requires a sequence of maven commands to build? Run make and have the makefile call maven.

Is that inefficient? You bet it is. Does it make it easier to sort out what to do to build a project for the first time? Yes it does. Does it make it 100% easier for our CI/CD framework to work with multiple languages and scan for the necessary compilers and dependencies? Heck yeah.

[edited for lousy formatting]

Re: Using Makefile(s) for Go

#23
post #17
post #3

I've seen a lot of developers, especially developers with C backgrounds, reach for Makefiles when approaching Go development, and I think it's a harmful practice. So, I'd like to offer a respectful but firm rebuttal to this article. :) I dislike using make(1) with Go for two reasons. The first is that make was developed for building C projects, and therefore is oriented around that task. Building C projects is a lot…

Why do people on this website use "make(1)" instead of "make" in writing? And I know it's in the man pages but what is this number even for?

The (1) in make(1) corresponds to which section of the manual[1] make is in. This is useful in some cases to distinguish between things that might be in multiple sections like printf(1) the user command and printf(3) the C library function. When everyone knows what's being discussed, I think it's mostly people trying to give a shibboleth that they've read the fine manual.

[1] - https://en.wikipedia.org/wiki/Man_page#Manual_sections

Re: Using Makefile(s) for Go

#24
post #3

I've seen a lot of developers, especially developers with C backgrounds, reach for Makefiles when approaching Go development, and I think it's a harmful practice. So, I'd like to offer a respectful but firm rebuttal to this article. :) I dislike using make(1) with Go for two reasons. The first is that make was developed for building C projects, and therefore is oriented around that task. Building C projects is a lot…

Make is available everywhere that matters, and is a simple declarative way to encompass build actions. What are the alternatives?

Bash? Not declarative, and requires lots more code.

Some go rewrite of Make? Not universal, possibly not maintained in the future.

Rake? Ugh, Ruby.

I strongly believe that make is the least worst way to build go projects, but please change my mind by suggesting some alternatives, not by complaining about the shortcomings of make.

Re: Using Makefile(s) for Go

#25
Not related to the contents of the article, but I love the font on that site. I also love the simplicity of the site as well. Nothing takes away from the ability to read. It is so clear and concise.

Re: Using Makefile(s) for Go

#26
post #6
post #3

I've seen a lot of developers, especially developers with C backgrounds, reach for Makefiles when approaching Go development, and I think it's a harmful practice. So, I'd like to offer a respectful but firm rebuttal to this article. :) I dislike using make(1) with Go for two reasons. The first is that make was developed for building C projects, and therefore is oriented around that task. Building C projects is a lot…

I agree. They claim `make` is simple, but it really isn't. PHONY targets are one example. Unfortunately I've looked for an alternative and didn't really find anything very good. I eventually settled on a Python 3 script. Python 3 is reasonably nice to use with type annotations. It doesn't require compilation and its speed is fine if you're using it to drive other build systems, rather than as a build system itself. W…

If you think Python 3 is a better tool than a Makefile you're entirely missing out on what makes "make" a useful tool.

The entire point is that it's not a general-purpose programming language, so that you're forced to separate the DAG aspect of your build process from any complex logic.

Sure, some things you can't do in a Makefile, and you generally shouldn't try. The Makefile should call some Python / Perl / whatever helper script to accomplish that particular task, separated by a process boundary.

By doing it like that you can seamlessly run your build process in parallel if you haven't screwed up in declaring your dependencies. E.g. some script that needs Python to fetch something from a database for the build will run concurrently with the build of the documentation.

It also encourages you to structure things in such a way as to have incremental and resumable compilation, e.g. Ctrl+C-ing the middle of a build in a project with a well-maintained Makefile won't require you to run the whole thing from the beginning, but that's typical of someone's home grown "I can do it better" monolithic "./build" shell or python script.

Re: Using Makefile(s) for Go

#27
post #3

I've seen a lot of developers, especially developers with C backgrounds, reach for Makefiles when approaching Go development, and I think it's a harmful practice. So, I'd like to offer a respectful but firm rebuttal to this article. :) I dislike using make(1) with Go for two reasons. The first is that make was developed for building C projects, and therefore is oriented around that task. Building C projects is a lot…

"For code generation and other ancillary tasks, Go includes the 'go generate' facility. This feature was created specifically to free developers from depending on external build tools. (https://blog.golang.org/generate)"

Please please please don't use go generate! While I respect your position, go generate is the worst and I hope they eventually deprecate it in future go versions. We tried go generate in some of our code and it went very badly:

* go generate is placed in a comment line. Comments should never be executable, they should be used for explanation. If I am trying to trace execution, I shouldn't be forced to scan through comments looking for side effects.

* from the go generate man page: "Within a package, generate processes the source files in a package in file name order, one at a time." You can't order your generate commands in a way you want, you have to order them using file order, or keep all of your go generate lines in the same file, which defeats the purpose of go generate.

* go generate will run every time, regardless of freshness of file. So if you need to run protoc or some other protocol buffer compiler, you have to regenerate it every single time regardless of whether or not it is needed which makes the build run way slower.

* What are the dependencies of this project? If I use go generate, I have to run some clumsy grep command to (hopefully) find all of the go generate comments in the package.

Sorry, go generate is to golang what COMEFROM is to INTERCAL. Please avoid if you can help it. If that means a shell script or heaven forbid a makefile, so be it.

Re: Using Makefile(s) for Go

#28
post #11

This... isn't even using the `make` part of Makefiles at all. If you look at the final example, every [1] rule is marked as `.PHONY`. `make` bundles 2 capabilities: a dependency graph and an out-of-date check to rebuild files. This demonstration uses neither. The author would be better served with a shell script and a `case` block. The advantages: - Functions! The `check-environment` rule is really a function call in…

I disagree. Make is more than a build system, it's also an automation tool. It gives you a fairly flexible format for managing different tasks with shared variables and autocompletion and more.

You can do it with a bunch of shell scripts too but I prefer having everything in a single file.

Re: Using Makefile(s) for Go

#29
post #21
post #8

Earlier quoted context omitted.

>A Go project should only require the Go compiler to build successfully. But they don't. The go compiler doesn't support yarn, npm, protobuf, open-api generators, doc generators like md2man, go-bindata-assetfs, gox and everything you need to complete the code generation done in modern Go applications. So how do you orchestrate this? People use Makefiles, bash scripts, go scripts and everything in between and combined…

>It's there and has been used for decades for a reason. I have always had problems to comply with such statements when the reasons aren't given.

either the parent edited his comment or you haven't read it, you're quoting the only sentence that hasn't got a reason in it.

Re: Using Makefile(s) for Go

#30
post #6
post #3

I've seen a lot of developers, especially developers with C backgrounds, reach for Makefiles when approaching Go development, and I think it's a harmful practice. So, I'd like to offer a respectful but firm rebuttal to this article. :) I dislike using make(1) with Go for two reasons. The first is that make was developed for building C projects, and therefore is oriented around that task. Building C projects is a lot…

I agree. They claim `make` is simple, but it really isn't. PHONY targets are one example. Unfortunately I've looked for an alternative and didn't really find anything very good. I eventually settled on a Python 3 script. Python 3 is reasonably nice to use with type annotations. It doesn't require compilation and its speed is fine if you're using it to drive other build systems, rather than as a build system itself. W…

PHONY targets are GNU extensions to make. They are not part of the make specification itself. POSIX make is really quite simple and it allows phony targets by depending on a target that has no dependencies or commands in it.
Post reply on HN