Taskfile: A Modern Alternative to Makefile
cloudnativeengineer.substack.com
cloudnativeengineer.substack.com
The syntax of Taskfile looks more verbose to me than Makefile, which is basically a shell script in disguise. To note also is that you praise Devenv because of it being based on Guix (Guile scheme), while you ditch Makefile which also embeds Guile scheme as the extension language (in latest versions).
Also to note, the opening argument, writing Readme files on how to build and use the software, has nothing to do with Makefiles at all. You could write a simple skeleton codegen that generates a Readme file skeleton or a template with those steps autogenerated in any language preferred, shell included, and run it as a Makefile target.
Op complained about that make requires a bunch of extra tools to get running, and suggests another bunch of extra tools, as well as of make DSL which is more or less shell on steroids, but suggests a mix of two different languages instead, and does not even seem to be aware he can use Guile Scheme to extend Make.
As to why being unhappy is probably not because it haven't been proof tested. I get your sentiment but feel the remark is out of place. I thought the rationale for picking other tools was straightforward and motivated.
Though the simplicity of make has been lost and I'm not sure the overhead is worth it. I wouldn't want to be the one introducing taskenv, direnv and derenv to a project.
But it was; one of his arguments. Yes, he opens with the argument that he was unhappy with the Make for years, but he later clarifies why:
> I am still stuck with a tool that (according to Wikipedia) has been written 47 years ago.
You probably haven't read his article to the end? Those were his words I copy-pasted from the blog. So one of the reasons he is unhappy is because Make is 47 years old. For some reason, it stopped to be good enough 20 years ago, according to his opinion.
I can give him that we can always have a better tool, but it is a tautology. Unfortunately, his offered alternative is not better IMO.
The reason is that they specify a boolean value as either "true/false" or "yes/no".
The last alternative causes problems not only for the Norwegian country code (no), but also in cases where you need to specify the actual word "yes"
You could of course end up with the same problem if you need to specify the literal "true" and "false", but by overengineering the acceptable boolean values, they increase the potential problem areas.
A Boolean represents a true/false value. Booleans are formatted as English words (“true”/“false”, “yes”/“no” or “on”/“off”) for readability and may be abbreviated as a single character “y”/“n” or “Y”/“N”.
Regexp: y|Y|yes|Yes|YES|n|N|no|No|NO|true|True|TRUE|false|False|FALSE|on|On|ON|off|Off|OFF
I feel like the Makefile syntax has some pesky warts, like not breaking when there are spaces in filenames, requiring tabs[1], the ceremony around PHONY targets... If you take the legacy stuff out of make,
[1] Apparently it can be fixed with a complex .RECIPEPREFIX incantation which behaves differently in different versions of GNU make, and was conveniently added just one minor version after Apple has graciously decided to stop updating all their GNU software versions.
Redo, ninja and tup seems like trying to do that, with their own twists, but none of them really managed to nudge make out;
But if people have colons in filenames, for example for timestamps, all bets are off, and make chokes spectacularly.
/s, in case it's necessary.
Which seems like a pretty reasonable trade-off for something that's free.
$ cat Makefile
a\ file: ; echo 123 > '$@'
$ make
echo 123 > 'a file'
$ cat a\ file
123http://savannah.gnu.org/bugs/?712
That being said… In projects I just avoid filenames with spaces in general
Even the fact makefiles have no extension is confusing to beginners. Everything about it screams outdated, idiosyncratic computing artifact and students pick up on that.
If someone can't understand how to use make perhaps they shouldn't be fiddling with C.
There’s a big difference in developing trust to contribute to a project and developing knowledge to start with a new language.
And I’ve worked with tons and tons of very amazing developers who aren’t comfortable with Make or even git because their careers have been with Visual Studio and other VCS. Therefore I don’t think it’s an adequate judge of skill.
Reading a manual, or failing to, is not gatekeeping. I learned how to write a Makefile in a few minutes, for free, with no one telling me I wasn't allowed to.
There is no "gatekeeping" argument here, just whingy babies that no one should waste 10 seconds caring about.
Nevermind that you ignored the part where I delineated between learning and using it in important projects.
I’m not even going to address the rest of your comment which is frankly a depressing outlook on life.
The easy on ramp was it was totally free and permissionless to learn how to write a Makefile, or read a bunch of existing ones, or countless howtos, and that a basic makefile is basic and you don't need to figure out the arcane bits until you want to, and at no point ever do you need a license no matter how easy or guided the path.
You can be a poor 10 year old in a 3rd world country and write a Makefile. No gates kept.
It's a depressing outlook on life to see something as simple a useful as make as some kind of problem where the supposedly superior answer is something ridiculous like python.
The fact that anyone even for one second thinks these are reasonable thoughts is what is truly depressing.
But putting that aside, you don’t think Make has a ton of footguns for someone starting out?
Spaces vs tabs for different bits because of decades of legacy? Oh but most editors default to tabs as spaces and suddenly no matter what you do, following tutorials will still fail.
Or you try and mess around with an existing open source project to learn from it. You want to list the targets? Oh wait, there’s no standard way to examine the makefile without understanding Make first.
If people consider Make easy to learn, imho they haven’t really reflected on how much more streamlined the rest of the software engineering ecosystem is, and how it benefits everyone from new programmers to experienced devs. I’m not the one saying it should be Python, I’m just saying Make is not a good solution in this day and age, when so many other ecosystems have shown they can have better on-boarding experiences.
Anyway I won’t respond further. Feel free to rage reply as you want.
Now whether learning how make works is the same as driving a car is left to the reader to decide. Either way make is also a societal form of cooperation with its rules and traditions.
Where does gatekeeping start and where does societal norms and cooperation begin?
I thought we all decided that norms and just imploring coders to hold the tools the right way isn’t scalable. I thought the industry had moved to safety through tooling, not safety through peer pressure and hoop jumping.
I mean, if we’re talking about cortical systems and safety, why is anyone using C at all?
the level of work needed to add a dependency in C+Make vs Rust+Cargo is huge.
Herein lies the issue for me: should existing projects switch because they are deemed to be gatekeeping when in fact either they are fine with their technology choices or they have not the resources or knowledge to change but they aren't gatekeeping. The external appearance is the same whichever reason might be the truth.
It’s how a lot of major OSS projects have shifted to cmake from make or scons. Granted cmake isn’t a pinnacle of ease of use either but it’s much easier imho to reason about and integrate with various tooling like IDEs
Ideals don’t have to be binary.
The on ramp to learn to drive is easy relative to actually being trusted/licensed to drive. But do I need to understand how an engine works to learn to drive? No. Maybe I needed to learn how gear changes worked in the day of manual transmission, but today with EVs and CVTs, why should learning stick be a gate to pass? That’s the equivalence of make to C
Regardless, all these analogies fall down because the crux of the issue is that if there are better alternatives that prevent issues, it isn’t sensible to keep using the old one.
Take your driving analogy. Should we force people to disable proximity sensors? After all you can drive without them but they make driving safer and easier for everyone.
Make is much the same way. It gets the job done, that doesn’t mean it’s good at getting the job done when better alternatives exist.
Make is easy for certain folks, yaml for others. If a project has been using make for years, should they be forced to replace it since someone believes they are gatekeeping? What happens if said project rejects a pull request that replaces said makefile with task file requiring python and yaml?
I'm pragmatic and also say if it gets the job done OK, but must I replace make with task if someone suggests I'm gatekeeping just because I have a pragmatic point of view?
But maybe it means that new projects use a more accessible build system. I’m not saying Task is the answer, I’m just saying Make is an issue for a lot of reasons like accessibility and portability to multiple toolchains and systems.
If you’re just coding for yourself, it’s not important. Nobody is saying you should change. But if you’re coding for an ecosystem, it’s always worth evaluating where changes can be made and then evaluating case by case if the pros outweigh the cons.
Make is the definition of portable for that job, so that was a weird argument to try to make.
I see a consistent pattern of unfounded argument from someone who has nothing more than "old=bad" or "new=progress"
I think your analogy is quite bad here. You ain't giving people proximity sensors or parking cameras or automatic parking. You are exchanging a good, comfortable vehicle we all know how to drive for a new type of vehicle with clearly worse ergonomics and forcing everyone to re-learn how to drive.
Once part of designing cars is that we can sit down in any car and just drive it. Perhaps we need to look up some minor and less important details, but we are guaranteed some very basic operation: how the blinking lights work, how to operate gas, brakes, steering wheel and manual transmission. I do think that they have messed up with short/long lights and back gears. That should have also been the same on all cars. But the basics are the same.
Makefile uses shell scripts for the recipes and shell variables, which makes Make an extension to the shell. There are differences, of course, but basics are very, very simple to get to learn. I learned 30+ years ago by just looking at other makefile and basically copying the details for my projects. I am still not even a very advanced makefile writer, I still lookup what automatic variables do, which one do I want and which functions I can use when I need them.
This right here. It goes back to my other comment about how those who are proficient with Make don’t understand how esoteric and arcane it is to beginners.
The attitude of “well it should be hard, the shouldn’t even learn C if they can’t handle make” is explains why my students love Rust so much. Rust has a sane and standardized build system they prefer to use. Make is the Stone Age to them.
And don’t get me wrong, they learn Make. They learn how to use it proficiently and we get through it. But at the end of the class, they will throw Make away and use another language like Rust. They simply do not want to deal with it.
Problem is if you care about the future, someone has to help the whingy babies become competent adults. Not everyone is a polymath autodidact that can absorb everything about computers like the HN crowd. Some rockstars start as whingy babies. If you don’t help them at that crucial time, the stay whingy babies, or worse, go on to become project managers. You don’t want the whingy babies managing your project.
There is no value in catering to them, since that generally requires blunt rather than sharp tools, and everyone else requires sharp tools to get actual work done. They can play with their toys, but they are toys thoight up by people who care about the wrong things. Anyone else should ignore and disregard them and their ignorant ideas.
Make is probably not the final evolution of a build sysyem for eternity, merely the arguments given for python of all the ridiculous possibilities, have all been garbage so far.
Now if a proposition included some sort of canonical flavor of python (or whatever language) that can be counted on to:
- exist on any system, and "any system" does not mean "any linux intall from today forward"
- could at least be added to any system if not already, ie, plain c source that compiles even on obscure old systems, or built out of something else equally ubiquitous like shell or awk.
- single simple binary
- all features included (no libraries of plugins and modules)
- spec advances only slowly and thoughtufully and without breaking compatibility
Make already exists, meets all of these points, and provides the means to express complex jobs or simple jobs without any special built-in magic support for any one language or framework of the month, especially not at the expense of any others.
Although, even a mythical "canonical python subset" would be a strange thing to propose with it's meaninful whitespace, when one of the main complaints about make is the tabs.
Make wasn't designed with any of that in mind. It was just one or a few people deciding the easiest path to code, or a style that they preferred.
It turns out most people don't find their decisions to be intuitive or explicable even decades later.
Actually, basic is very simple, since the syntax is pretty much like the shell, or I mean, it is more or less shell scripts on steroids.
It fails only in a certain part of the script where tab is part of the syntax. It is like saying Python language fails if I add a wrong number of spaces, or replace a space with a tab, since white spaces are part of the Python syntax.
> Even the fact makefiles have no extension is confusing
How is that confusing, the entire filename (makefile) is "the extension" :-)
Put .mk at the end of the file name if that makes you happy and less "outdated" and "idiosyncratic". C/C++ does not require extension either, it is just a convention. Ever seen <vector>, <algorithm>, <iostream> etc?
DSL are the plague of our field, for one that is useful like SQL, you have a hundred that turn your life into hell. Make's DSL is already full of gotchas, but templated YAML based DSL are just insanity. They have so many footguns, and so little flexibility and ability to be debugged.
After trying a lot of task runners and builders, I finally settled on doit:
https://www.bitecode.dev/p/doit-the-goodest-python-task-runn...
- It makes the simple things simple, and the hard things possible.
- It scales up (you get targets, dependencies, manual scripting, etc), but it also scales down (you can define a simple list of shell commands to run and that's it).
- Instead of a DSL, you have regular Python. Which for all it's faults, is an excellent scripting language. It has great tooling support, and comes with a debugger.
- To avoid people abusing the expressiveness of Python, the default syntax to create tasks encourages a declarative style.
I've been using doit for years, and every single project I introduced it to was improved by it.
Of course, it comes with it's own problems (non python dev can dislike having to install that, and it doesn't yet provide a stand alone executable), but the ROI is way better than the alternatives I tried.
[1] https://www.bitecode.dev/p/installing-python-the-bare-minimu...
The same can be said about using general purpose programming languages to specify stuff declaratively, but instead of implementation the problems happen in each and every single use of it. It only takes a single user that either does not comply with standards or feels they are particularly clever to mess everything for everyone.
The truest statement of all of HN
It started off with configuration files, but then we needed logic, control flow, or variables; and for some reason nobody goes "ok, maybe we need an actual progamming language now" and instead we've created horrors beyond our comprehension.
Azure Pipelines is the worst I've used: three types of variables, conditional statements return string "True" and "False", and of course it's mostly undebuggable.
However, for now there is just a single implementation in GO.
I came up with basically the same thing as CUE (as described in [1]) on my own, except for the heavy schema stuff.
I think the schema stuff is unfortunately what's going to keep it from adoption.
I mean, I didn't add schema stuff explicitly in, but you can define schemas in mine, which makes it easier.
But making it explicit raises the bar for an implementation and may scare people away from writing one.
In most cases I think it's the fault of the creators of the YAML DSL (e.g. ansible) but with CI pipelines people really just should be writing scripts 100% in other languages and wiring them up to events with YAML. I dont have any sympathy for people using them as programming languages.
I'm sure Microsoft is happy to indulge it though because hey, vendor lock-in.
Anything else is shenanigans.
I just prefer Python to make, it's easier for me and most colleagues I met to write and to debug. And making a custom Python function when I need more than bash inside the dodo file is great.
But for someone used to make, why not used it?
The best of both worlds is a good programming language that allows expressing domain specific libraries almost as if they were domain specific languages, without going so declarative that people don't know how to script anything anymore.
I don't know if Python/doit really achieved that, but its task definitions seem almost as readable as the YAML, in a real language where I can do anything I want without asking permission. So I would always choose doit over Taskfile.
But a better comparison is between doit and make. I much prefer make's basic syntax, although I dislike newer features which inevitably have stretched the syntax beyond its original simplicity, but conceivably you can achieve something similar to make in a language but as a library rather than a DSL, and it would be immediately friendlier and more powerful than make. Depending on what I was doing I would use doit, but for something simple I've still got make (and it would be easy to convert if desired, in fact I have some projects in mind that could do with this!).
But imagine having this in CI instead of YAML files.
I've tried the 3 of them, eventually I found CUElang was the best balance between expressiveness, ability to enforce and check for constraints, and easy of use.
But I would welcome any of them replacing the YAML mess we currently have in sysadmin.
I wish more projects would compile their Python tool into a stand alone executable so that the users wouldn't have to deal with the installation process.
I will at some point write about how to do that with Python, so that the knowledge will more broadly available and maybe the community will grow the habit of doing it.
Meanwhile, I don't have more to offer than the "Relieving your Python packaging pain" procedure: https://www.bitecode.dev/p/relieving-your-python-packaging-p...
It's not perfect, but it's an improvement for any people that don't do a lot of Python but has to.
The mentioned "follow-up" article: https://www.bitecode.dev/p/back-to-basics-with-pip-and-venv (if you find the time, please add a link to it to make it even more complete)
Substack doesn't really have any way to make a series of articles, or to bundle stuff together, so linking manually is the only thing you can do that will give a little structure.
One more reason to move off it when I find the time.
Ruby package management is lacking in many ways. E.G: there is not good build in solution to manage projects and multiple versions of ruby on windows, it's all 3rd party. And C-extensions are still terrible to install
The apparent simplicity comes from the fact the ruby ecosystem is much smaller than the Python one, with 95% of it revolving around ror, so you are more often on the happy path and see less errors.
But:
- Half of the devs in the world are on Windows.
- Installing doit with pip is not trivial. Hence the "Relieving python packaging pain" procedure: https://www.bitecode.dev/p/relieving-your-python-packaging-p...
And most people will never ever encounter this article, so they will hit a missing command, a missing package, a PATH error, a PYHONPATH error, a version error or something like that. They may even try to solve it by copy/pasting many commands from the web, some of them with admin right, and make a mess of their setup.
That's why, for end user tools like doit, providing a stand alone executable is still the best solution.
It's not really a language where you can write something and be confident it'll still work in ten years with the latest release of Python that'll be shipping with distros.
People had 15 years to move from 2 to 3.
I had to port dodo files from 2 to 3, it's a 10 minutes job. Most of the time, running 2to3 on it does the job automatically.
After all, you only use the stlib for task runners, delegating a lot of work to bash, and they are quite short.
> It's not really a language where you can write something and be confident it'll still work in ten years
I still have Python 2.7 projects running fine, and reinstalled one serving half million users a few month ago.
But even without this, Python was created in 1991, and Python 3 was released in 2008, that's 17 years between first release and the first big migration. To compare, go is 14 years old. In total.
Plus your build file will have been edited so many times by the end 10 years anyway.
Make has a lot going for it, but this is a terrible argument.
They are usually fixable of course. Assuming the thing worked on the authors system at all, it's possible to diagnose and fix it up for the current environment. But that has to be done, and sometimes takes more than a few minutes to hunt everything down.
And then one day I started seeing these pyc directories appearing out of the blue, right in whatever directory any script happened to be in. The same scripts that didn't do that yesterday. What an awesome thoughtfully engineered system!
The argument was not terrible, it was plain lived experience fact.
Unfortunately it’s named the same.
Enter magefiles, I could use the same language in them as I could with my other code and stuff just worked.
Having to install doit first is indeed going to be a barrier of entry for a lot of people.
It's still worth it in my experience, but I get how it can deter users.
I have never tested it enough to discover things it does wrong, but given how new and not exactly popular this tool is, I bet there are lots of things it doesn't do right...
So, I have no idea why would anyone want this tool... beside just using one extra infra tool to appear as if you know something others don't (and that's exactly the kind of people who use it at my workplace).
I used Task on a new project and the explicitness of it all was a breath of fresh air. I mean which is clearer between:
BINARY = ./foo
GO_FILES = $(shell find . -type f -name '*.go')
# Rebuild BINARY only if GO_FILES have been modified
$(BINARY): $(GO_FILES)
go build -o $@
.PHONY: build
build: $(BINARY)
and env:
BINARY: ./foo
tasks:
build:
cmds:
- go build -o $BINARY
sources:
- **/*.go
generates:
- $BINARY
?You can add the embedded files to the "sources" list, no issues
> Why rebuild the binary if you've only touched test files
Yep, this is a thing not currently supported by Task, but they're considering extended globing iirc (with negation, like in gitignores). I think you could do something like that though:
env:
SOURCES:
sh:
find . -name *.go | grep -v _test.go
kinda ugly, but easy to understandIt's like Greenspun's tenth rule, only for build tools.
the makefile
$@, .PHONY, and the implicit target/dependency syntax is clever, terse, but NOT clear.
To find out which tool is more simple, we could just count core concepts or use docs-length as a proxy. But 50 years of makefile's will muddy the water here.. is it super complicated or just well-documented? At minimum, when you reach for makefile then you know the concepts it brings are stable if complicated, and the tool is available by default almost everywhere, and the tool-versioning is almost totally safe to ignore. Looking at taskfile, I don't know the schema for the config-yaml, and more importantly I have no real confidence it's stable over time. Worrying about that sort of thing for project libs is enough work, so people don't want this kind of trouble in their build system. I also know taskfile isn't available by default until I install it.
Build-system yak-shaving mostly comes down to the same stuff. A group of people who don't want to learn tools try to convince everyone to learn some different tools, failing to realize that most of us are lazy in the same ways. Don't want to learn makefile tips and tricks? No one does really, but no one cares about taskfile tips and tricks either. Given the basic equivalence between these two situations.. there's definitely some wisdom here in preferring the standard rather than the startup.
Looking at your example I think I understood why there is two schools for thoughts about this (and similar ones): because different ecosystems (go/rust vs C/C++/ObjC, mostly in these days, but also Fortran, etc) have completely different build needs.
Golang and Rust (and node.js, AFAIK) almost doesn't need external build tool, as they have their own (go, cargo, npm). It is very easy to make receipt for such build in any, even very limited, DSL, with any "task running" software.
On the other hand, how will Taskfile look for complex C/C++ project? You want separate on-demand compilation of each source file separately ("${CC} ${ALL-SOURCES}" is baaaaad idea), you need dependency extraction (ugly part of any Makefile, I admit, but receipts are well-known), you need linking, you often need some resource processing, or yacc/bison generation.
I don't think, Task file with all these features will be less ugly than typical Makefile. And many tricks are non-existing for Taskfile (and it is not clear, are they possible at all without extending DSL), and well-known for Makefiles.
This difference leads to polar views on such software.
Thank you for example!
(I hear svn may have improved merge tracking, but I'm not going to spend more time on it.)
git branch -c [old] new
git checkout -b new [old]
git switch -c new [old]
git rebase old new # if new only exists upstream?
You have to remember how each command works separately, because any one will lead you astray about some of the others.I still think that most people working with VCS today would've been better off using SVN. Most companies use Git in a centralized way anyways -- they don't need the distributed feature of the system. Most people using Git today don't know how to go back in history and wouldn't be physically able to carry out this task if requested because most Git repositories I've seen in my life were, essentially, write-only, just a glorified rsync.
I needed many years of working in infra to come to terms with Git and become efficient at using it. Most developers using it today will not have a multiple-year rotation in infra position, and are terrible at using the tool.
But this is very different from Taskfile vs Makefile. Git came with many attractive features. Some, perhaps inconvenient or demanding in terms of learning, but it was a genuine new tool that solved a problem other tools at the time didn't (branches were very expensive in SVN, sharding repositories across large distances was very expensive in SVN, and Git solved both).
Taskfile doesn't do anything better than any of the existing tools. It doesn't even promise anything like that... It's just "like make" but worse... so, what's the point exactly?
> Technology moves forward
Oh you sweet summer child... no, it doesn't. Sometimes it moves backwards, sometimes it jumps in place pretending to do useful work... there's no rule to say it should, and the evidence abounds of how this statement isn't true.
Even if you have a single centralized remote, git being distributed lets you work offline, because you have a full copy of the repository, and you can commit locally.
> Most people using Git today don't know how to go back in history and wouldn't be physically able to carry out this task if requested
Google exists and can help if someone doesn’t know but needs to do that.
> because most Git repositories I've seen in my life were, essentially, write-only, just a glorified rsync.
[citation needed]. If you have more than one person, how can a repository be write-only?
Google isn't going to help if you were doing merges for a year, and then discovered you need to go back in history, and now you realize you should've been doing rebases instead. Google all you want -- it will be of no use, because your repository is a pile of garbage at this point, and it's not humanly possible to go back in time. Like I wrote in the post you replied to -- most programmers use Git where rsync would do.
> git being distributed lets you work offline
You could do that with SVN too. It was actually my other complaint about Git. Both SVN and Git use the word "commit", but those two mean different things. SVN "commit" is more like Git "push". Perhaps, you'd have to work harder in SVN having to reconcile the changes you haven't committed for a while, esp. because the tradition was for many developers to work on the same branch, but not impossible.
> If you have more than one person, how can a repository be write-only?
Do merges instead of rebases. When I say "write-only", I mean that the history of the repository is worthless -- it's written, but it's not actionable. This isn't apparent with smaller teams who have very few branches and work on a relatively small project. With larger teams merges in a repository create a ball-of-yarn history. I mean, you can read it, technically, but even using it as a log isn't feasible anymore. You could just throw it away, and nothing will change in your workflow / ability to recover from bad code changes / ability to transfer patches across versions etc.
this is not one of those times
Make operates on the abstraction of targets are files. If you don't confirm to that abstraction, make is the wrong tool. To use the hammer analogy, just because you're good at hammering something, doesn't mean you should use it for a screw.
Tools like docker and terraform don't use conventional files for state. Running tests in e.g. golang doesn't generate output files. Logging into the GitHub cli or aws don't have input and output files to compare for whether the task needs to be re-run.
The purpose of the make utility is to determine automatically which pieces of a large program need to be recompiled, and issue the commands to recompile them.
And from the response you got you failed at it. Were you promoting it because technology was "moving forward" and it was "time to move on"--whatever that means, instead of its merits? Didnt work for git, certainly won't work for Taskfile.
So, don't be what guy? The idiot that says branches have no benefits? The lesson is that there are dummies on both sides of the table.
Evaluate technology on merit. And, anything that is to dislodge a ubiquitous tool that's been around for 47 years has a high bar to cross.
Taskfile brings nothing to the table. It cannot compete with existing automation tools. It doesn't even promise to be better than existing automation tools...
I mean, give me at least some reason to believe it could be better than what I already have... to me it looks like the people who wrote it simply never saw what's already there. Kind of like a university project where students write a simplistic compiler for a subset of C language... It's nice that they tried their hand, but they have a long way before they actually produce something useful.
- copying a database from production to local for bug reproduction/fixes - inserting seed-data into the local database - creating a migration after changing records - applying the migration - rolling back the migration - running a tool to copy configuration from a keyvault - running the end to end tests - generate certificates - set up an ssh tunnel to a remote server or database
All of these things are part of the development process, and cannot be part of the build system. Each of them has their own commands and tools.
All developers need to easily be able to do these things, without asking help each time, or learning the syntax of each command.
One easy way is to stuff all of them in a task runner with simple aliases. Another could be writing a document that you can copy/paste from. I prefer a task runner, since you can memorize the alias and work from the terminal without looking things up.
Also, what makes you think a build system doesn't or shouldn't do those things?
Also, you could just write a Shell script to do all of those -- what's the difference?
$ mkdir bin
> easily be able to do these things, without asking help each time
$ ls bin
> generate certificates
$ bin/mkcert.sh
What you didn't need: yet another tool and config syntax as a dumping bucket for your one-liners and command aliases.
Maybe you're thinking of k8s and all of its related tooling?
I am guessing the primary selling point is that it's sort of human readable similar to Markdown but even that (in fact, especially that) falls short when a configuration is more than just 5 lines, in my opinion. Trying to figure out which bullet this sub-bullet belongs to by holding my finger on the screen while scrolling is not an improved experience. And don't even get me started on the clever type system.
I don't see this as any worse than figuring out where nested parentheses are closed: tooling helps. I'm almost certain there's a whitespace plugin equivalent of Rainbow Parentheses for your IDE/editor of choice
Well . . . the Perl folks, I think, came up with something less pointy than XML. And we all appreciated that because we'd all poked ourselves with XML too many times.
None of them are relevant, or made any case for using it instead of just using the thing described in an international standard that is shipped by default in UNIX and Unix-like OSes for decades.
It's like their selling point is to talk crap about other tools without making any case for their own offering.
I fail to understand what point you tried to make. There are already plenty of Make implementations for Windows. If you think that installing a third party tool like Task is ok, I don't see how doing the same with a Make implementation would exclude it as a valid alternative.
I just pointed out there are Make implementations for Windows.
And I'm not even mentioning WSL or MinGW.
I don't know what else to say.
Just because Windows decided to do everything differently to Unix and you decided to use Windows doesn't mean everything else has to adapt to be "cross platform".
FWIW, GNU Make runs fine in Windows if you're allergic to WSL
Ut is a solution for the vast majority of cases. Even most Windows environments use WSL.
We have few colleagues working on react-native who use windows, and it is easy to support them because it uses mvdan/sh for script execution.
Being able to organize tasks into multiple module specific taskfiles which can be merged (with auto-prefixing) into the top level taskfile is quite nice. The support for watch mode, timestamp checking, environment propagation with built in dotfile support are all nice features that we found very handy.
It would have been nicer if it used hcl but I can live with yaml. I am glad that it doesn't use a real programming language - it makes it easy to look at a task in isolation (we have hundreds) and figure out what it depends on and what it does.
It is easy to be dismissive of such tools when glancing at the homepage, but it is one of those tools that grow on you when you give it a chance. The author has also been addressing a lot of common pain points over the years for which we are thankful - it is nice to be able to trigger tasks from any subdir (similar to npm run ...) and the new editor integration is quite too.
I see people saying that you can do all that this tool does using a few simple scripts. But these scripts only start off as simple, once you need a watch mode, cross platform support, timestamp change detection, selective argument propagation, need to exec dependencies in right directories etc. they tend to get buggier and messier.
I wanted it easy to read, write, parse and use, so I'd use YAML. You guys all shit on YAML because it's extremely badly used by, say, Helm charts, but it's a good markup language in itself. StrictYAML is anyway.
So I started writing a toy Makefile in YAML with these requirements: I need to run a command, with variables, it needs to depend on another command, and shouldn't run if the "output" file is already present and newer than the "source" files. So something like this:
env:
APPNAME: app
clean:
run: rm -rf ./dist
build:
depends:
- clean
input:
- **/*.go
run:
- go build -o dist/$APPNAME
output:
- dist/$APPNAME
At this point I realized I had written exactly the same things proposed by Taskfile (which I heard of but thought "meh it sucks, Makefiles are great).So yeah, Taskfiles are just Makefiles with a sane syntax and better tooling (JSON schema, auto generated doc, real default target...). I have no idea why the comments are so hostile towards it, because clearly Makefiles are not perfect (it's even a pretty bad format)
it didn't run on CI because it uses GNU Make instead of BSD Make on my MacBook
What? $ /usr/bin/make --version
GNU Make 3.81
Copyright (C) 2006 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.
This program built for i386-apple-darwin11.3.0I hate make and I dont like the idea of using non-typesafe YAML to build this (which presumably this tool does, since it's done in golang).
I've been using a "standardized" bash script with a huge case statement as a justfile/this/makefile replacement but it's not ideal.
Also, a lot of those look like artificial complexity over a shell script sourcing another one containing only variables export, and running the required scripts.
If your Makefiles look like this:
build:
go build -o dist/app
You might as well just use make, it's fine. It's less fine when you want to avoid rebuilding if sources didn't change, or if you want to use environment variables, maybe from a dotenv file, or if you want to do conditions (like adding a debug flag to a build command if a make variable is set), or if you need to use make incantations (hoping they'll work with both BSD Make and GNU Make), then a Taskfile might help solving some issues, including legibility.Your description starts to be more interesting, and I feel like mk (the successor of make from the Plan 9 people: https://plan9.io/sys/doc/mk.html) might address those, but again, I don't have a need for it.
One of the few formats in common use that's actually worse than Makefile, plus you need to go and find another binary to run the thing.
1. Syntax. Makefiles aren't parseable and have some at least unexpected syntactic properties.
2. Dependency detection: AFAIK any make will always use a timestamp and the precision varies a lot. A checksum or at least guaranteed resolution would be great. Also, you can't depend on directories.
3. Dynamics (this is going to be contested): Changing dependencies and tasks at runtime is possible with GNU make, but not ideal.
Taskfile improves on problem 1 but probably worsens everything else.
- Taking environment variables into account as a dependency (they may affect Makefile logic and also code generation by tools that interpret them).
[^1]: https://www.gnu.org/software/automake/manual/html_node/Multi...
I've been at a few places that insist on still using them. They mostly work fine, but having PHONY littered everywhere gets a bit annoying.
I don't think a successor can exist. Makefiles seem like the last of a dying breed - not because make is bad, but because there are better ways to accomplish this already.
The market for people who've outgrown Makefiles but haven't embraced the alternatives has to be pretty small...
I want to keep the solver, which is pretty good. I want to keep the idea of targets, rules, recipes, which is what tools like Taskfile throw out that makes them not very attractive to me. I even want to keep a good chunk of the syntax for specifying these. But Make needs some kind of way to invoke it that switches off all the dumb shit.
- parse any reasonable whitespace indentation, for chrissake
- -r by default, or some way to opt into relevant sets of rules (e.g. `import "c"` to get CC, CFLAGS, %.o, etc.)
- Separate def syntax and namespacing for proper file-generating rules vs. phony rules, no more .PHONY or fake dependencies; also move stuff like .INTERMEDIATE or .PRECIOUS to the rule def.
- .DELETE_ON_ERROR, .ONESHELL by default, probably others I forgot
- better semantics for double-colon rules; once you know what the phony rules are you can make them operate in this mode by default and give better syntax for pre/post hooks, priorities, run-on-failure, etc.
- something solving the same problem as but less dumb than .SECONDEXPANSION (guile helps somewhat here but give me some help for the most common cases)
- stamp files implemented by some out-of-tree cache, they're just an optimization and I'd rather lose them now and then and have to rebuild, than have to keep adding them to every other tool's ignore lists.
As well as some actually new features to help manage targets without local inputs, e.g. smarter connectors for fetching dependencies from network resources.
https://people.redhat.com/rjones/goals/ http://oirase.annexia.org/rwmj.wp.com/2020-01-goals-preview....
https://plan9.io/sys/doc/mk.html
It's basically the same with some niceties and good defaults. One of them is the introduction of attributes, the most prominent being virtual: a target can be virtual meaning mk will never check the existence of a file and always run the recipe.
However it's an extremely hard sell to bring into a corporate tech stack today for either brown or green field projects.
I've gone with lua and libuv because the concurrency model is so trivial. Each process spawn goes in a coroutine, stderr and stdout go into lua strings, use yield when the process is still running. However that comes with pretty minimal linting or error detection on the input, so maybe use some other language if you want that.
Or rephrasing, instead of a DSL for describing a graph and transforms to apply to the nodes, you can use a library in your favourite language instead. You might like that more than debugging cmake.
Just add a makefile to your repo and anybody is able it get started. I would have no clue what todo with a project in the structure proposed.
Note: I just makefiles as an entry point to the underlying build tool such as gradle or npm or go build
Why not Make? We actually used that on a few projects previously, but found it challenging to use with both our teams and with typical tools used in the PHP and JavaScript stack.
1. Most of our newer hires have never used Make before, or anything like it. It seems to have fallen out of many curriculums in favour of other build tools, presumably due to decreasing focus on C / C++ in fundamental computer science classes. The idea that a build tool is inherently files based is a completely foreign concept. 2. File-based dependency tracking being the exception, and not the rule, simply works better with the majority of the types of tasks we run. Those include building docker containers, pulling down CMS databases for local development, and running CLI tasks like database migrations. 3. For those tasks that can be tracked with files, Task supports both timestamps and hashes.
One big win of Task over other tools is that it ships it’s own built-in sh shell. If you have tasks that run on a host and not in a container, it significantly reduces your host dependencies. Being a typical single-binary go app makes deployments easy too.
I’ll also say the maintainers have been very responsive to our few bug reports and feature requests, and responsive to our contributions too.
The worst part is yes, it’s another YAML DSL to learn. And personally, I wish Make had gained more traction in the web and docker world. But given all of the above, the trade offs of switching to Task have been worth it for us.
Also, I disagree with Make being a bad task runner. In fact, I find it quite versatile. The fact that it is not a DSL with a meta programming language or, in the case of Taskfile, a YAML document that gets convoluted quickly (see also: Ansible) is a feature IMO.
Used with an alias it can be a wonderful way to manage scripts.
I use mine as a sort of global bookmark for shell commands I can't be bothered to memorize all the params for.
Handling env files is also a huge help.
> Taskfile is just a dialect of YAML format with a specific syntax
I imagined that. Ok bye
Just write a js or python script. Or even a shell script
Makefile syntax is bad but frankly YAML was a bad idea. (slightly less than XML sure)
Taskfile might be great for many use cases, but yes, why not just write a shell script. Almost every time I use a CI system, I end up falling back to just having it run a shell script, rather than relying on many of the build in features, because they are almost always a bit contrived. The same can be said for Makefile replacements, they try to predict what people need, and in the end it ends up being a fancy way of running a script.
YAML is ridiculously complex, with a bunch of surprising behaviour.
I would take XML over YAML any day.
But when you start getting into why are some things properties, or values, XSD or what not, then no.
Nobody has time for those thick XML books
You cannot choose to use YAML without the string 'no' becoming boolean false.
Plenty of people complain about the complexity with XML, and there is a valid argument there. Using it as an argument in favour of YAML is kind of ridiculous.
It has cache facility to speed up re-builds. scons --implicit-cache I heard people say, Scons is slow, but it need not be if we use cache facility.
Some people say Meson (https://mesonbuild.com/) would be its successor, but I use scons for building my C/Python files. (ex: Python to .mpy files, asciidoc to pdf, html etc)
At my work we used it for a fairly large source tree and no-change incremental builds would take a good minute for scons to figure out there's nothing to do.
These days we use something based on google blueprint which generates ninja build files.
Bonus: you write your shell commands in a file that's aware of what's correct/incorrect.
If your module in app/constants.py defines PORT which app/server.py serves and app/client.py connects to, how do you drop that same constant into your Makefile or docker-compose.yaml?
For me, literally everything including my docker, docker-compose, caddy, nginx, and of course the application code and all auxiliary scripts are presented as Python modules to solve this exact problem.
So my .env file just “includes” (automatically via autoenv) what’s in my scripts directory along with setting any environment variables when I’m jumping around my shell. Therefore I can just write a function like “dbup” or “devstart” and have those locally aliased to whatever they summon in the current project
Better documented, better designed and architected (taking more use-cases / edge-cases into account), composable, easier to debug, fewer dependencies.
> Taskfile: It’s PHONY Makefile in Yaml
Makefile is for make, which “makes files”.
You don’t “task files”
*file in general is wrong. Of course it’s a file