This is the very same narrative that vim users proclaim: "It's totally not complex if you took some years to learn your tools" — while simply not realizing that the whole Make syntax is a pile of clunks and hacks and stopgaps and bandaids that has a super steep learning curve and looks like incomprehensive gibberish to the uninitated eye.
And most of that needlessly so IMHO. It shouldn't be hard to find a better, well designed syntax with sane defaults, stricter rules with typing and linting, less surprises (tabs, I'm looking at you) that's overall much less scary for newcomers — as well as less taxing for "expert casual" users like me. (I still go for my coworkers and Stack Overflow regularly if I have to fix one and I'm a fucking CTO with 10 years of dev experience)
Make's only reason for survival is its ubiquity. No matter which terminal you boot up, it's just there, that's why we use it too, and I hate it every day.
I think none of the alternatives (cmake, Jam, Gradle, Maven, grunt etc. etc.) has really succeeded as a cross-platform, cross-ecosystem solution that's both an approachable, simple and usable version of a DAG execution engine that's usable for lots of situation.
So the situation will probably stay like with vim and the QWERTY keyboard, the status quo pretty bad, but everyone's too used to it to go for something better.
Something interesting I see more often than not when someone I know decides to try out vim:
They know I'm a vim user so they ask me for good tutorials and I tell them there are lots out there but they should start by running vimtutor (included with vim). Takes around 15-20 minutes to work through the interactive lessons and gives you all the basics of movement and editing.
A day or so later I ask how it's going and the answer is usually that they're struggling with vim, so I ask if they ran vimtutor. Usually nope, or they started it but quit after a minute or two.
Why would one switch to a tool with a different paradigm, ask for and get a tutorial recommendation, and then ignore the advice of spending less than 1/2 hour on a tutorial that ships with the tool? sigh
I think that I am rather good at memorizing lots of obscure raw data - but in my experience it takes a lot longer to remember all the "obscure" commands of vim. In this sense I would not say it takes 15-20 minutes, but at least a day if at the end you really want to have the commands in your brain.
In this sense the mistake in your argumentation is the timescale that you are talking about.
Also, I hold some fairly controversial opinions: you don't need dozens of vim plugins; you don't need to compete with vim golf champions; you don't need an extensive configuration.
What you do need is to fully grok the idea of normal mode as an editing language with verbs, objects, and modifiers. Once you "get it" then you have a working knowledge of how to construct "sentences", and learning a new text object allows you to apply pretty much all verbs to it. Same for learning a new verb; you can apply it to lots of things.
Over the years I've learned a lot of interesting vim things and I've forgotten most of them. Because I don't need them every day or even every week. That's ok. The things I remember are things I use often, because they're useful in everyday editing.
In Vim there are modes that describe sets of keybindings and there are commands that are bound to them. The commands themselves are are straightforward for anyone who's fluent in Linux vocabulary.
'Saving' is a Windows word. We :write to files or :w for short.
Sounds like a solid reason why makefiles are great to me then.
Vim is so good it finally is letting emacs-OS have a real editor.
But the question is whether this is Make's fault for being incomprehensible, or the User's fault for being lazy.
The thing is virtually any system or tool can be understood with enough practice, but just because there is someone out there who can use it well does not mean it's not poorly designed.
Both C and Make are decades-old technologies, but most people can read a C file without much experience and intuitively understand what is going on. The same is not true for Makefiles.
While they might think they know what's going on, I'm not convinced they actually do. C can be an incredibly subtle language to get right...
http://blog.llvm.org/2011/05/what-every-c-programmer-should-...
Regarding make, the thing to realize there is that it's just a DSL for specifying a dependency graph (and the way for satisfying dependencies). It's not a hard concept, but unless you come to the field with a bit of computer science background, it's not likely something you've been directly exposed to at the level required to understand make. But I don't know that this means make's choice of abstraction is necessarily a bad thing for its intended use cases. I also think this tends to put the lie to the notion that the difficulty is mainly around tabs... tabs may obscure the details, but switching to a clearer syntax won't solve the more fundamental gaps in understanding the mechanics of make.
I'll also argue that this is a potential problem with DSL's in general... because DSL's often don't use traditional imperative language semantics, there's always a bit of a learning curve that needs to be ascended. However, for make at least, there are a useful debugging tools to help understand what it's actually doing.
For me, at least, 'make -n' is great in that it instructs make to resolve the dependency graph based on the current state, and then just print the commands it'll run without executing them. If you know what the commands do, and know what needs to run, this can be a huge help understanding what's going on. (And there are other more powerful debugging capabilities as well for dumping variables, etc.)
As with many 'simplified' DSLs that are often unsuited to the task at hand (e.g. C++ templating), make (or versions of it, anyway), evolved to accommodate these use cases and then morphed into something that accidentally became turing complete: https://stackoverflow.com/questions/3480950/are-makefiles-tu...
Isn't a C++ template the exact opposite of 'Domain Specific'?
Make is not a needlessly complex tool (unlike most garbage tools these days); it’s complex because it needs to be flexible enough to build any programmer’s software. Since ways to program and build software are infinite, the tool itself cannot be simple, otherwise trade-offs have to be made.
'make' exists for what it's capable of doing and it must be so as it's used in every piece of software and operating sytem everywhere.
So it's extremely popular/widespread, therefore it must be good, and therefore you should use it (thereby making it even more popular/widespread)?
I really hope that you poorly worded that/I'm grossly misunderstanding you because otherwise, your logic is batshit insane.
My experience has been that that people that truly understand and appreciate technology for its own sake are also likely to underestimate the value of a tool being popular and widespread. What 'popular' really means is that the tool is more likely to be where you need it to be and the people you need to use it will already know how. Those things outweigh a 'perfectly' designed tool that you don't have or know how to use.
There's a balance point, for sure. For instance, the pace of "innovation" in front-end javascript libraries throws out a lot of babies with the bathwater, and we might be well-served by accepting a paradigm for 10 minutes and building on it rather than always chasing after the next hot thing.
But at the other extreme, there was a time when PHP was ubiquitous for back-end development, and it stuck around largely because of inertia: the obvious flaws were accepted just because it was so accepted as the tool for that job. But I don't think many people are lamenting the fact that we've moved on to better tools for back-end development. In that case inertia was a bad thing because it delayed innovation.
For me an example of a "good enough" tool is GIT. It's not perfect, it's got a non-trivial learning curve, and surely some innovations could be made to improve version control. Some would already argue that Mercurial is the Betamax to GIT's VHS - but it does the job well enough, and everybody uses it, so it's good enough.
I'd argue Make is more like PHP than it is GIT.
Exactly this.
> I'd argue Make is more like PHP than it is GIT.
What would you want a make replacement to look like?
Make is pretty 'roll your own' in places, but I've found it to have a nice balance between expressiveness and terseness. For simple projects, there isn't much to do in make, and for the more complex projects I've been involved in (embedded work, a self-hosting compiler, etc.) make can still generally express the build process within the confines of Makefile syntax. (In contrast to more modern tools that might require plugins in a different language, etc.)
For the compiler project I mention, the build process includes building several executables in C, running them against a pre-built VM image to compile another VM image, translating that image into C code that is then compiled and linked into the VM to produce another executable, after which there's a unit test suite. Root rebuild command is 'make clean && make tested', and there are only a couple hundred lines of Makefile code to get there. (There's also cross-compile support, etc.)
I'll admit that there was a certain amount of effort involved in writing those Makefiles, but I've also implemented the same build process using a modern GUI tool and found it to be much more difficult.
Admittedly I haven't given a ton of thought to this specific problem, but here goes:
I actually like Make from a conceptual standpoint. I think it's a good idea to codify build orchestration in code rather than non-human-readable output from a GUI application. My main gripe is that it seems to be very much a 1.0 tool: it was build to solve a specific problem, and then features were added as needed. I would love to have an iteration on that concept which benefits from what we've learned from Make while rounding off the rough edges of what feels like the result of a largely ad-hoc design process.
As far as what that would look like: I would like to see a concise, low-level API for doing the kinds of things Makefiles do (i.e. defining a dependency graph, setting up build parameters etc), and better tools for composition.
Also I would sacrifice a bit of terseness for the benefit of readability. For instance an arbitrary keyword like `Phony` could be replaced by a more extensible name-spacing syntax which lets you know exactly what's going on.
* Recursive Make Considered Harmful
* Changes to the Makefile do not implicitly trigger rebuilds
* Dependencies on directories is not really possible
* Lots of output by default
* Rules with multiple output files are not really possible
* can't handle spaces in pathnames
[0] https://github.com/qznc/annoying-build-systems#make-alone
* Change tracking entirely depends on modified timestamps instead of something more robust like checksums.
* Makefiles are always platform specific. Make executes commands based on a dependency graph, but it has no commands of its own. You either write multiple makefiles or you build everything in a bash environment.
I think in the context of Make it is ok. If you rely on checksums, you must implicitly assume that all commands you execute are idempotent. That assumption easily breaks.
Ceterum censeo, Clearcase must be destroyed.
Does it hurt to write it though? It makes the makefile more readable for people who don't know it's built-in. Besides, it's only built-in for ".c". I prefer to keep it in order to have a generic Makefile I can adapt to other programming languages.
Make has lots of built-in rules for lots of languages, C, C++, Fortran, Lex, Yacc, etc.
CC = clang
CFLAGS += -Wall -Wextra
libs = -lm
objs = hellomake.o
hellomake: $(objs)
$(CC) $(CFLAGS) -o $@ $(objs) $(libs)
.PHONY: clean
clean:
rm -f hellomake *.o *.d
deps := $(objs:.o=.d)
%.d: %.c
$(CC) $(CFLAGS) -MM -MF $@ $<
-include $(deps)
The first two lines tell us which compiler we're using and set up some extra compiler warnings. Then we specify which libraries we use (it's a matter of taste whether this is a variable or just spelled out later) and the names of our object files.The first target, hellomake, is our executable. This is the default target, so it's the one that'll be built if we run make with no arguments. It runs the C compiler with the flags we specified earlier. Its dependencies are the object files, which means they'll be built automatically and rebuilt when their dependencies change. As noted, there is a built-in rule to build the object files from the C source using our CC and CFLAGS. We don't need to tell make how to do that.
The next target, clean, is a "phony" target—it doesn't refer to a file. Normally a make target tells make how to build a file. A phony target, in contrast, is really just a shell script that doesn't produce any file output. This one deletes all the build products.
The remaining lines set up the dependency tracking for headers. This makes it so that a change in a header file will trigger a rebuild of any source that includes it. We begin by making a list of files by copying the list of object files and swapping in ".d" for the ".o" file extension. Next is a rule that tells how to build a .d file from a .c file. This rule runs the C compiler and tells it to output a dependency tracking file. The last line says to include each of the .d files in the Makefile. (The .d files contain Makefile syntax to specify the header dependencies.)
I omitted the IDIR and ODIR and such because in a project of this size I wouldn't bother putting includes and object files in separate directories from the source.
I hope that helps. I know make can seem outdated and weird but I think it really can be simple once you learn a few things about how it works. Hopefully this will get you started in that direction!
Don't use wildcards in clean target. Use $(RM) $(OBJS) $(DEPS) instead.
Please honour LDFLAGS and LDLIBS here.
> $(CC) $(CFLAGS) -MM -MF $@ $<
Please honour CPPFLAGS here.
Doesn't that sacrifice portability, though? Is this method portable between GNU make and FreeBSD make and OpenBSD make?
The special handling is in the compilers: they emit make file parts!
[1] cf. http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ma...
This indicates that make is hard to understand. I've been using it for a while and I still struggle to grok concepts like order-only prerequisites.
Here's a couple examples:
1. The "%.o: %.c" line is a huge code smell for Makefiles. You shouldn't ever write that line—it's built in!
2. Mentioning .h files in the Makefile. Don't. Go look up the `-MMD` flag in your C compiler. Clang, gcc and many other C compilers support it. Add that and a "-include *.d" line in the Makefile and you'll have great dependency support.
Everything else in that makefile is just setting variables. It's made overly complicated by the directory junk, but I'm failing to see the problem.