It's not really a Go thing but a build system thing. It's useful to have your build system know what is an "executable target" and how to run it.
Bazel does this for _all_ languages. I'm sure most other modern generic build systems do too.
AFAIK "go run" is trivial and for single-file scripts it's fine. But for more complex cases (like the NPM equivalent thing) I actually think it's a bit of a shame that it's even needed. I don't really know why we have per-ecosystem build systems (Maven, Go, cargo, whatever the hell you're supposed to do in Python these days, the nebula of web front-end tooling, etc etc).
Admittedly I do not really know the ins and outs of any of these systems in detail. I'm sure there are some good reasons why they exist under the hood.
I'm not sure there really is a good technical reason they exist. It's cultural. It basically goes like this:
- the inventor of new language 'coolang' has a way that they make their project
- it's kinda messy, so they clean it up into a tidy script with a few clear and straightforward commands and/or flags, and give you "cool build," "cool install" for making sure all the necessary dependencies are present, etc
- a community builds around coolang organically
- everybody is so used to running "cool build" that that's just how it's done. New features get added around these conventions
It's cultural, that's all it is. But like all small tight knit communities, it's important to understand the culture of the community in order to engage with it on its own terms. Its just humans being humans.
There's also a technical reason, which is that the build system is written in the language it targets. So the cool tool is written in coolang. That's obviously not required, you could use any programming language for the cool tool, it just happens that all people that care about the cool tool, understand the needs of the ecosystem, have issues with missing features etc. already have a non zero intersection of languages they know of: they all know coolang.
If coolang decided to try to add coolang support to Bazel instead, they would probably have to learn Java[1]. Current maintainers or contributors to Bazel don't know coolang, and they don't care about it much, especially in the early stage. And maybe coolang developers don't know Java, or even actively hate it with a passion (that's why they were on the market for a new language). And even if some coolang developer decided to contribute to Bazel, the barrier would be much higher: being a mature build system with so many features and different needs, surely working in it is going to be complex; there will be many different concepts, and layers, and compromises, and APIs to work with. So for them it just makes more sense to use coolang so that all coolang developers can contribute to it having a real need for the cool tool to improve.
[1] I know nothing of Bazel. So just bare with the example even if it's technically not correct.
A nit (hopefully a welcome one given that it supports your statement) is that Bazel's rules are written in a language called Starlark, which has python syntax just without classes and a bunch of limitations surrounding switch statements and loops.
The core of your point is correct: who wants to both support an additional tool chain and an additional language for building things? Terrible sell.
Go itself is a little bit of an edge case because they recommend leaning on Make, but ironically they do not use Make for its intended purpose and all the (actually good) functionality that Make gives you is reimplemented from within the go compiler.
The build system should come with it. Even if it requires me to follow conventions, it’s magnitudes better than rolling my own. I can add to it if I need to.
The worst offenders are C and C++ projects. Make? CMake? You’re on your own. During development, it’s so good to be able to just runtime run source like in go and bun.
As an embedded developer I shudder to think of all of the work that would go into /runtime run source/ to have it build objects, link them into some kind of format, convert the format to a series of flash addresses and data, connect to my JTAG over a network, halt execution, erase the flash, load the executable file into flash, verify the load, and try to signal a PMIC or other chip to reset the device to start it back up.
Or you could just read "Linking a single object file" from the GNU Make manual's catalogue of rules, which describes how to do exactly what you want with the caveat that you still have to run the program after it's built and linked: https://www.gnu.org/software/make/manual/html_node/Catalogue...
You have to do that work anyway... Why not encode it in your build system?
(Admittedly I have never tried this with a modern build system. My real world approach for that would be a janky phony rule in a Makefile, with a bunch of MAKE_VARS the user has to set on the cmdline/in the environment to set up the toolchain/serial port etc. But in principle I have always believed it should be possible to make this process as easy as compilation)
Are those vars discoverable? As in can you infer what they should be? If you can’t, or are building for all unknown possibilities, wouldn’t it be safer to enumerate known var values to perform that?
Why must everything be explicit? Wrap it all up into one var.
cmake -DBUILD_FOR=esp32 .
Then provide your own little runtime.sh.
For other languages and build systems, a runtime run sourcefile is the easiest way to get someone going, leave the edge cases out. Just get it running so they can hack on it.
No, you have to manually document them and then the docs go out of date and you can't realistically set up CI for this little runner script so certain use cases get broken and blah blah blah and it sucks!
Same for runtime.sh.
This is why the 'runtime run source' is so useful! I'm totally on board with it. I just don't think we need one implementation per ecosystem.
One runner per project (the 'make run' approach) is worse than one per ecosystem, which is in turn worse than just having one for everything!
As I stated, for C/C++ - folks like their build systems the way it is.
For everyone else, they want runtime run sourcefile and can then build scripts around that to package it up how they want to. For local development, I shouldn't have to wait 40 minutes for a compile to see a div change.
The toolchains that would benefit from being able to run quickly, compile quickly, are what we want. Having to do 15 steps to get your code on an embedded device is just part of the territory. The rest of us have pipelines.
have you ever used tinygo? I have been curious how much that project gets used. It seems to me that rust is probably going to be the language of choice some point in the future.
I looked at that recently for a project I'm working on, but walked away when I found that important parts of the net package are pretty much nonexistent on ESP32.
You know, like net/http, for example...
I might have misread the docs, but somehow I doubt it.
The Go standard library's net/http package doesn't yet compile due to some dependency issues, but tinygo provides its own net/http package to stand in as a replacement[1].
Make and CMake are not examples of what I was talking about!
In Bazel (and, I assume, other similar systems), you can just "bazel run target" and it always works. Doesn't matter what languages the thing is written in.
Make and CMake are certainly not like what. (Yes, you can have a "run" target, but that's not the same thing).
So my minor gripe is that 'runtime run source' does not need to be a per-runtime thing.
I am not sure actually, there is this but it doesn't seem to support C++ or Python in the way build_cleaner does, which sounds like it would indeed be kinda annoying: https://github.com/bazelbuild/bazel-gazelle
> I'm sure there are some good reasons why they exist under the hood.
User expectation's, mainly. In fact, Google didn't even use the go tool internally, which I expect hasn't changed, using Google's build system à la Bazel instead. It was created only for the wider audience.
bazel is pretty cool, I have seen it work at Uber for the go monorepo at impressive scale (and of course it works for google). When I need to scale up the build process this will be the tool i reach for, but for starting out it is another technology that someone would have to learn.
I think the issue with Bazel may be that it works extremely well at Google where for 80% of the code at the company, building is just a completely solved problem, with amazing tooling integration, and it's glorious.
Whereas in the open source there is much more manual setup to get it working smoothly. And the manual setup is much easier in the tool your ecosystem already knows.
So it may not actually be a wonderful system in and of itself (outside of the Google monorepo). My comment was mainly about the principle rather than an endorsement to adopt Bazel!
Bazel does this for _all_ languages. I'm sure most other modern generic build systems do too.
AFAIK "go run" is trivial and for single-file scripts it's fine. But for more complex cases (like the NPM equivalent thing) I actually think it's a bit of a shame that it's even needed. I don't really know why we have per-ecosystem build systems (Maven, Go, cargo, whatever the hell you're supposed to do in Python these days, the nebula of web front-end tooling, etc etc).
Admittedly I do not really know the ins and outs of any of these systems in detail. I'm sure there are some good reasons why they exist under the hood.