6 ms·
I'm not a fan. It works well for what it is, but what it is is an additional language to know in a place where you probably already have one lying around. Also
by __MatrixMan__ 2y ago
I'm not a fan. It works well for what it is, but what it is is an additional language to know in a place where you probably already have one lying around.
Also, like make, it encourages an imperative mode for project tooling and I think we should distance ourselves from that a bit further. It's nice that everybody is on the same page about which verbs are available, but those verbs likely change filesystem state among your .gitignored files. And since they're starting from an unknown state you end up with each Just command prefixed by other commands which prepare to run the actual command, so now you're sort of freestyling a package manager around each command in an ad-hoc way when maybe it's automation that deserves to be handled without depending on unspecified state in the project dir.
None of this is Just's fault. This is people using Just poorly. But I do think it (and make) sort of place you on a slippery slope. Wherever possible I'd prefer to reframe whatever needs doing as a build and use something like nix which is less friendly up front, but less surprising later on because you know you're not depending on the outputs of some command that was run once and forgotten about--suddenly a problem because the new guy can't get it to work and nobody else remembers why it works on theirs.
- nerdponx 2y agoI mainly don't understand how Just is any better than a run/ directory full of executable shell scripts.
- zwerdlds 2y agoThat works too. I've done both and I currently use Just because it collects the entrypoints to the project into a single file. This can provide an advantage where there's a bit of interdependence across your entrypoints. E.g: You have a docker container, you might be `run`ning it, `exec`ing it etc. from the same compose-file. So Just gives you the ability to link those shared commands within the same file. Once the entrypoints get too numerous you can either break them into scripts (I do this partially depending on the level of behavioral complexity in the script) or partition your justfiles and import them into a single master.
- pdimitar 2y agoIf that works well for you, use it. I did that for 10+ years and got fed up with having to remember which names I gave to my scripts that month. I gradually evolved my views and that got reflected with the names of the scripts. `just` helped me finally move away from that. Now I have i.e. `just check` in projects in different languages that all do the same thing -- check types and/or run various linters. I can go in a directory and run `just check` and I know I have taken care to have all checks that I want in place. Similarly I can run `just test` and I know I'll have the test suite ran, again regardless of the programming language or framework. Absolutely nothing wrong with a directory full of scripts but I lost patience for having to scan what each does and moved away from them.
- lijok 2y ago> Now I have i.e. `just check` in projects in different languages that all do the same thing -- check types and/or run various linters. I can go in a directory and run `just check` and I know I have taken care to have all checks that I want in place. Similarly I can run `just test` and I know I'll have the test suite ran, again regardless of the programming language or framework. How is that different from having a scripts dir, and a script called `check` or `test`? How is `just -l` different to `ls scripts`?
- pdimitar 2y agoI believe I already addressed that this is purely a matter of taste and convenience, not sure why you are not reading my comment and are asking for more. And it was already said: if you like it more, use it. Nobody is holding a gun to your head. And I even explained that I used that in the past and moved away from it.
- akoboldfrying 2y agoI also haven't seen in your previous response how Just is better than a subdir with shell scripts named according to a convention. AFAICT, the productivity improvements you described came exclusively from using a consistent naming convention, not from Just. And since everyone's dev env supports subdirectories with shell scripts already, why not simply use that instead of requiring Just?
- BeetleB 2y agoWell, for one, your recipes can be in another language (e.g. Python). You can build complex recipes out of simpler ones. Sure, you could do that by creating a new shell script that calls other shell scripts, but then you're reinventing just. You don't need to be in the directory to run those scripts. I think a better question for you: What's the benefit of putting .PHONY recipes in Makefiles, when you could just have a directory full of shell scripts. If you find yourself using .PHONY recipes, then you already have a reason to use just.
- e12e 2y ago> Well, for one, your recipes can be in another language (e.g. Python). Surely this is true for stuff in a ./bin or ./scripts folder - binaries, python with shebang etc?
- e12e 2y agoAh, I see there's: https://just.systems/man/en/shebang-recipes.html https://just.systems/man/en/shebang-recipes.html Which could be done in shell, but typically rather be limited to oneliners (invoking awk) rather than piping a heredoc to an interpreter.
- akoboldfrying 2y ago> You don't need to be in the directory to run those scripts. There's already an easy way to solve this: $PATH. > I think a better question for you: What's the benefit of putting .PHONY recipes in Makefiles, when you could just have a directory full of shell scripts. If you find yourself using .PHONY recipes, then you already have a reason to use just. Well, I think it's the same question, rather than a better question. And the answer is yes, if all you need from make, now and in the future, is a set of .PHONY targets, then by all means just use shell scripts. make is used because often you need slightly more than this -- or you may do so tomorrow, and don't want to change the syntax you use to accomplish tasks.
- BeetleB 2y ago> There's already an easy way to solve this: $PATH. I have 10 projects. Each with their own set of shell scripts. You want me (and all other developers) to pollute the $PATH with 10 directories? And then you have a namespace problem. I usually have a "test" recipe in my justfiles. The analog would be a test.sh file. But with your solution, it will have to be projA-test.sh and projB-test.sh. And if I dump them all into the $PATH, how do I quickly see the scripts relevant to a particular project?
- brushfoot 2y agoAgreed, particularly if you pipe to fzf. (For those who haven't used it, fzf is a fuzzy-searchable menu for the command line. You pipe lines of input to it, and it shows them in a menu. You start typing and it fuzzy searches the menu and selects the best match. Then you press Enter to pipe that out, or Tab for multi-select. It's fantastic.) I have convenience functions in my profile script that pipe different things to fzf...scripts, paths in the current directory to copy to the clipboard, etc. It's indispensable. Bonus: progressive enhancement. If someone doesn't have fzf/those convenience functions, it's just a directory with shell scripts, so they don't have to do anything special to use them.
- slightwinder 2y agoIt's a different approach, none is better or worse, people simply have preferences. And all other features aside, it seems to be able to call commands from any subdirectory in a project, which is actually nice compared with a normal shell. I mean, you can replicate this with some lines of shellscripting, but not everyone seems to maintain an elaborated $BIN of personal tools.
- gregwebs 2y agoI do both. A script directory helps provide the underlying extensible tools. Just is a convenient ui for the most frequent dev use cases of a project.
- bbkane 2y agoI agree, but `Just` as an incremental improvement is a much easier sell to teams than asking them to think about their builds completely differently and rewrite everything to fit that. Offering a cave man a flashlight is probably more helpful than offering them a lightbulb and asking them to wire up the cave to power it :D
- __MatrixMan__ 2y agoIt is definitely a very fine incremental improvement over make. It's just incremental progress in a direction that I don't want to be headed.
- pdimitar 2y ago1. The language is extremely simple and is consistent. 2. I agree on having to move away from imperative and go for declarative (if the latter was what you had in mind) -- any ideas for a better tool that does that and is just as easy to learn? 3. RE: cobbling together stuff with and around `just` is relatively trivial to fix f.ex. I have my own `just` recipes to bring up the entire set of dev dependencies for the project at hand, and then to tear them down. It's a very small investment and you get a lot of ROI. 4. RE: Nix, nah, if that's your sales pitch for it and against `just` then I'll just strongly disagree. Nix is a mess, has confusing cutesy naming terminology, has a big learning curve and a terrible language. All of that would be fine, mind you, and I could muscle through it easily but the moment I received several cryptic error messages that absolutely did not tell me what I did wrong and I had to go to forums and get yelled at, is the moment I gave up. `just` is simply much easier and I am not worried about not having Nix-like environments for my projects. Docker + compose work very well for this. Finally, your point about an obscure single command that people forget about in the future applies to literally any and all task runners and dependency managers, Nix included. That's not a valid criticism towards `just` IMO.
- __MatrixMan__ 2y ago1. It's a fine language but I have all kinds of "works on my machine" problems with it because it has no associated dependency manager. Other languages solve this with lockfiles and such, and it's likely that you're already doing that with one of those same languages in the same project. So just... Use the main language for whatever it is. 2. No, nothing's so easy, but you can get more if you're willing to work for it, and I think the juice is worth the squeeze. 3. For runtime state, I find that using just as a wrapper around Tilt or docker-compose or k3d or whatever just hides the perfectly adequate interfaces that those tools have. The wrapper discourages deeper tinkering with those tools. It's not a particularly difficult layer of abstraction to pierce, but it doesn't buy you enough to justify having an additional layer at all. 4. In the case I'm thinking of, the whole team was working happily because they had used a Just recipe to download a file from a different repo, and then somebody removed the recipe, but everyone (except the new guy) had the file from months ago, which worked. Nix wouldn't have let us accidentally get into a broken state and not know it. It would have broken as soon as we removed the derivation for the necessary file. I sent him the file through slack and then he was able to work, and only discovered later how it got there on my machine. That kind of uncertainty leads to expensive problems eventually.
- o11c 2y ago> like make, it encourages an imperative mode for project tooling and I think we should distance ourselves from that a bit further. Um, what? `make` is arguably the most common declarative tool in existence ... Whenever people complain about Make in detail, it's almost always either because they're violating Paul's Rules of Makefiles or because they're actually complaining about autotools (or occasionally cmake).
- joshuamorton 2y agoMake isn't, at all, declarative. It's almost entirely based on you writing out what to invoke, as opposed to what should exist and having the build system "figure that out". That is, in make you say `$(CC) -c foo.c -o foo.o`, which is telling you, ultimately, how to compile the thing, while declarative build systems (bazel/nix/etc.) you say "this is a cc_binary" or "this is a cc_library" and you let it figure the rest out for you.
- GuB-42 2y agoIf your executable is named "foo" and there is a "foo.c" somewhere, your Makefile only needs to contain "foo:" and make will figure out how to build it using its default rules. If you have more than one file (ex: foo.c and bar.c), just write "foo: bar.c". Modern build systems are more advanced and have better defaults, but the general idea is the same. They are all declarative. An imperative build system would be like a shell script.
- o11c 2y agoYour "declarative" systems are no more declarative - they just hide the commands/flags being used, far worse than `include config.make`.
- __MatrixMan__ 2y agoIt's quite easy to accidentally write makefiles that build something different when you run them a second time, or when some server that used to be reliable suddenly goes down. Or when the user upgrades something that you wouldn't think is related. It does no validation of inputs. So suppose you're bisecting your way towards the cause of a failure related to the compiler version. Ideally there would be a commit which changed the compiler version, so your bisect would find a nice neat boundary in version history where the problem began. Make, by contrast, is just picking up whatever it finds on the PATH and hoping for the best. So the best you can do is exclude the code as the source of the bug and start scratching your head about the environment. That willingness to just pick up whatever it finds and make changes wherever it wants with no regard to whether dependency created by these state changes is made explicit and transparent to the user is what I mean by "imperative".
- AlotOfReading 2y agoI find declarative build systems end up pretty frustrating in practice. What I want from a build often isn't the artifacts, but the side effects of producing the artifacts like build output or compilation time. You get this "for free" from an imperative tool, but represents a significant feature in a declarative system that's usually implemented badly if it's implemented at all. The problem gets worse the smarter your tool is.
- thfuran 2y ago>What I want from a build often isn't the artifacts, but the side effects of producing the artifacts like build output or compilation time You frequently build things not to get binaries but to spend time compiling?
- AlotOfReading 2y agoThe point is that there's often no way way to express "I want side effects" in declarative tools, and the number of side effects that might be useful is vast. For example, sometimes I profiling the build times to see where I should focus effort. Sometimes I want to see it to quickly check for issues where adding some dependency header causes build times to explode 100% in downstream dependencies during cold builds. Another common occurrence for is trying to debug a platform, toolchain, or standard library issue and the build system either doesn't detect changes in those components or only makes the components readily accessible in an internal cache that's subject to invalidation issues. You'll usually get the wrong artifact or test results in those cases. Some other systems (e.g. bazel/blaze comes to mind) actively try to hide side effects like stdout. In all of these cases, the only way to actually get these side effects is to reach into the tool's internals by blowing away caches/output folders or reading live log files. That's a failure of the build tool.
- MereInterest 2y agoI haven’t used it, but it sounds like make’s —-assume-new flag does exactly what you want for the first part. It lets you rebuild everything that would result from a changed file, including all side effects, without needing to first update the file.
- eru 2y agoIf you don't want to buy into the whole Nix philosophy, you can also use something like 'shake' (https://shakebuild.com/ https://shakebuild.com/) to build your own buildsystem-like command line tooling.
- BiteCode_dev 2y agojust is not meant as a built tool, just a task runner. Those have vastly different goals. make is a build system: it has targets, it has file deps, a dag resolver, etc. But a task runner is basically a fancy aliaser with task deps and arg parsing/proxing. And just is good at being that. Although I agree I not a fan of adding yet another DSL.
- zahlman 2y agoMaybe because it's been many years since I used C or C++ for anything serious, but I don't get that impression from using make in the first place. I haven't seen it used for setting up a build environment per se, so there aren't any "packages" for it to manage. When I've written a Makefile, I saw it as describing the structure of cache files used by the project's build process. And it felt much more declarative than the actual code. At the leaves, you don't tell it to check file timestamps; you tell it which files' timestamps need to be up to date, and let it infer which timestamps need to be compared and what the results of those comparisons need to be in order to trigger a rule. Similarly, a rule feels composed of other rules, more than it feels implemented by invoking them.