Hacker News new | ask | show | jobs
by maccard 765 days ago
I think this post is well intentioned but misses the point entirely.

    GOOS=darwin GOARCH=arm64 go build -ldflags="-X 'main.Version=v1.2.3'"
Executing this with variables for os/arch/version is not straightforward in a makefile. You might argue that it's a flaw with the `go build` command, but replace go with cargo/dotnet/maven and you have the same problem.

> AFAIK, Just doesn't have any way to mark "this task is complete, so don't re-run it"

What does a makefile look like for "install this list of dependencies with pip/homebrew/apt, and don't run it twice?"

> It also doesn't have file-based dependendicies, which are pretty common for any kind of programming

C and C++ compilers at this stage are the only places that I work with file based dependencies. Unless you explicitly declare all the headers as dependencies in a makefile, make won't rebuild a cpp file if you change a header. IME ninja (plus cmake to generate it) has entirely superseded make in this space. Tools and languages comes with it's own build tool (see above - go/cargo/dotnet/etc) or state tracking (docker) that break make's assumption of file based dependencies.

> Why would I want to lose all of Make's flexibility and power in exchange for slightly prettier UI syntax?

Because every make file I've ever used in recent memory has been 30% workarounds to avoid file based dependencies, and work around subtle footguns that make has, rather than actually doing what I want it to do - execute a build command.

3 comments

> What does a makefile look like for "install this list of dependencies with pip/homebrew/apt, and don't run it twice?"

It looks like this:

    default: your_task
    
    VENV_DIR := venv
    
    $(VENV_DIR)/bin/python: requirements.txt
        python -m venv $(VENV_DIR)
        . $(VENV_DIR)/bin/activate && pip install -r requirements.txt
    
    your_task: $(VENV_DIR)/bin/python
        do_your_thing
    
    clean:
        rm -rf $(VENV_DIR)

> C and C++ compilers at this stage are the only places that I work with file based dependencies. Unless you explicitly declare all the headers as dependencies in a makefile, make won't rebuild a cpp file if you change a header. IME ninja (plus cmake to generate it) has entirely superseded make in this space. Tools and languages comes with it's own build tool (see above - go/cargo/dotnet/etc) or state tracking (docker) that break make's assumption of file based dependencies.

In our Python example above, you have a file-based dependency between requirements.txt and your virtual environment. And then you have a file-based dependency on your virtual-environment's Python for whatever task you're trying to run.

> Because every make file I've ever used in recent memory has been 30% workarounds to avoid file based dependencies, and work around subtle footguns that make has, rather than actually doing what I want it to do - execute a build command.

Why would you want to avoid expressing your dependencies? File dependencies exist, and are the simplest / most correct model for 75% of all build automation. When somebody has to sit down and read your Makefile, isn't it nice to have a way to express "these are the files that actually matter for task X"?

I think this post is well intentioned but misses the point entirely.

    GOOS=darwin GOARCH=arm64 go build -ldflags="-X 'main.Version=v1.2.3'"
Executing this with variables for os/arch/version is not straightforward in a makefile. You might argue that it's a flaw with the `go build` command, but replace go with cargo/dotnet/maven and you have the same problem.

What makes that hard? I think the following works correctly:

    $ cat Makefile
    goos := FOO
    goarch := BAR
    mainversion := BAZ

    .PHONY: build
    build: ; @GOOS=$(goos) GOARCH=$(goarch) go build -ldflags="-X 'main.Version=v$(mainversion)'"*

    $ make -n
    GOOS=FOO GOARCH=BAR go build -ldflags="-X 'main.Version=vBAZ'"
> I think this post is well intentioned but misses the point entirely. > > GOOS=darwin GOARCH=arm64 go build -ldflags="-X 'main.Version=v1.2.3'" > > Executing this with variables for os/arch/version is not straightforward in a makefile. You might argue that it's a flaw with the `go build` command, but replace go with cargo/dotnet/maven and you have the same problem.

This is pretty easy in Make, especially since GOOS and GOARCH are environment variables. One way to do it could look like this:

    export GOOS ?= darwin
    export GOARCH ?= arm64 
    VERSION ?= 0.0.0+$(shell git describe --dirty --always)

    build:
        $(if $(VERSION),,$(error VERSION was not set))
        go build -ldflags="-X 'main.Version=v$(VERSION)'"
you could run it as:

    $ make build (uses darwin/arm64/0.0.0+<git ref>)
    $ make build GOOS=linux VERSION=1.0.0 (uses linux/arm64/1.0.0)
    $ GOOS=templeos GOARCH=sparc make build VERSION=1.1.0 (uses templeos/sparc/1.1.0)