4 ms·
> Note: use tabs rather than spaces to indent the commands in your makefile. Otherwise Make will crash with a cryptic error Using this as an example, are there
by barbazoo 4y ago
> Note: use tabs rather than spaces to indent the commands in your makefile. Otherwise Make will crash with a cryptic error
Using this as an example, are there more modern equivalent tools that may be a bit more user friendly? I appreciate make and I get its age and the complexity and all, it's just sometimes I need something that's simple and explicit, without the historic baggage.
Edit: It's not about spaces/tabs obviously, but about "Otherwise Make will crash with a cryptic error" which I used as an example
- imran-iq 4y agoSince make 3.82 (which is about 12 years old now) supports `.RECIPEPREFIX` .RECIPEPREFIX = > all: > foo bar
- enriquto 4y agoalso, since forever, you can use a semicolon and put the rule on the same line: target : deps ; rule There are certainly a lot of legitimate criticisms of make, but the tabs issue is ridiculous (as are complains about .PHONY).
- kortex 4y agoTabs are absolutely a valid criticism. I can't tell you how many times the editor I'm using put in 4 spaces instead of a proper tab. .PHONY is truly a bizarre construction. There's nothing like it in modern code systems. It's only by familiarity with make that it gains some semblance of normalcy.
- Akronymus 4y agoI'd say Shake fits your needs. https://hackage.haskell.org/package/shake https://hackage.haskell.org/package/shake
- dxhdr 4y ago> Using this as an example, are there more modern equivalent tools that may be a bit more user friendly? I appreciate make and I get its age and the complexity and all, it's just sometimes I need something that's simple and explicit, without the historic baggage. Indenting with tabs too complicated? Let me introduce you to YAML.
- barbazoo 4y ago> Indenting with tabs too complicated? I meant "Otherwise Make will crash with a cryptic error" and used that as a single example.
- hedora 4y agoI've found the "make tabs are confusing" crowd heavily overlaps the "python is more readable than perl" crowd. I find the cognitive dissonance amusing.
- lmm 4y agoBoth views come from the same place: the idea that the semantic scoping should match the visual scoping, rather than counting tabs for indentation but not counting visually indistinguishable spaces (make) or ignoring your indentation entirely in favour of what the braces said (perl).
- int_19h 4y agoYAML prohibits tabs outright. That sounds a lot simpler to me, since there's no need to be aware of the difference between a tab and the equivalent number of spaces when the former is not a possibility at all.
- hawski 4y agoI don't understand why and don't know when spaces were started to be used for indentation. It never made sense for me. Your code editor surely supports setting tab width if you are not happy with the default. If the answer is vertical alignement I will weep and offer pity, but no sympathy. Even if that is the case you can still indent with tabs and aligns with spaces if you like to torture yourself, others and your RCS' history.
- lanstin 4y agoI have preferred spaces to tabs since the mid nineties because everyone sees it the same. With tabs people use 2 4 and 8, which can make my tendency to over indent code cause more trouble. I will say I have been doing go recently and have been forced into tabs and camelCase, and have found consistency really is better than The Right Way. For make, since using protobufs and code gen and being allergic to checking generated code in, have found make to be nicer than I remember it.
- burnished 4y agoIm no historian but I suspect white space sensitive languages influenced the desire to drop a white space character.
- chubot 4y agoAnother commenter and I both wrote some simple wrappers around Ninja in Ruby and Python -- I would recommend that approach https://news.ycombinator.com/item?id=32303692 https://news.ycombinator.com/item?id=32303692 I started from ninja_syntax.py and then improved it based on what I needed. Ninja is fast and simple, but it's (intentionally) lower level than you might want. https://www.oilshell.org/blog/2022/10/garbage-collector.html#declarative-ninja-mini-bazel https://www.oilshell.org/blog/2022/10/garbage-collector.html... It does seem like there should be a "standard" thing that is maybe not so C++ specific (like CMake and Meson which both seem to use Ninja), i.e. for the use cases in the blog.
- drothlis 4y agoI love Python + Ninja! I was going to link to my article on Ninja + OSTree, but I found in your blog you've already seen it. :-)
- chubot 4y agoOh cool, well it's worth linking here! https://lwn.net/Articles/821372/#objects https://lwn.net/Articles/821372/#objects I linked it from https://www.oilshell.org/blog/2021/04/build-ci-comments.html#ninja-and-ostree https://www.oilshell.org/blog/2021/04/build-ci-comments.html... How is OSTree working out? Are you still using it? It seems cool, but for some reason I don't see it mentioned very much, maybe because it's focused more on embedded system image uses, rather than "the cloud" which seems to be more popular on this site? I would like to see Docker/OCI containers "broken up" into just file formats and just content-addressed data/networking. Not a weird registry with a different local API than remote API. It should be more like git ! Not sure if OSTree fits there -- as far as I remember, it's inspired by git but it's focused on a different use case. This recent project seemed interesting but it's also for more of a machine learning use case: https://news.ycombinator.com/item?id=33969908 https://news.ycombinator.com/item?id=33969908
- drothlis 4y agoFixed your link to the full article: https://lwn.net/Articles/821367/ https://lwn.net/Articles/821367/ OSTree is still working very well for us. At the time I wrote the article we had been using OSTree (and the build system I described in the article) for 4 years; 3 years later not much has changed. In those 3 years we have started building images for different architectures and our build system / OSTree handled it just fine, as you'd expect (it already handled cross-compilation, but for a single architecture). Our build system also builds Docker containers (for our cloud services) but instead of a Dockerfile we use a custom yaml format that lists the base image (like "FROM" in a Dockerfile), the apt packages to install, and the commands to run (like "RUN"). Then we create a lockfile of all the apt packages, download them (into a local OSTree repository, but that's an implementation detail), and install them with a custom "docker run" command + "docker commit". We end up with the base layer + the apt layer + a single layer produced by concatenating all the RUN commands + a layer with our compiled Rust binaries and Python files. We use apt2ostree to generate the lockfile (really it's our patched version of aptly doing the work) but we use docker (not OSTree) to build the Docker layers. We use Docker's standard push/pull mechanisms to deploy these containers. To hook this up to Ninja we use "marker files" (a file in the build directory) to track whether this work has been done (e.g. you need to regenerate the apt layer if that layer's "marker file" is older than the lockfile). Our Python "configure" script (i.e. our build system) looks something like this: def build_docker_container(name): yamlfile = f"{name}/docker-base-image.yaml" with copen(yamlfile) as f: data = yaml.safe_load(f) ubuntu_version = data["ubuntu_version"] image = docker_pull("ubuntu:{ubuntu_version}") image = docker_apt_install(image, data.get("apt_dependencies"), lockfile="%s/Packages-%s-amd64.lock" % ( name, ubuntu_version), ubuntu_version=ubuntu_version) cmd = "..." # RUN commands from yaml deps = [...] # dependencies from files listed in yaml return docker_mod(image, cmd, deps) (Where "copen" is like "open" but it tracks which files have been read by "configure" itself, to detect if we need to re-run "configure"). What I really like about the Python+Ninja combo is that you can pass these targets around as Python variables — you don't have to come up with an explicit filename for each one (the target name is generated by each helper function, e.g. `docker_pull` returns "_build/docker/${name}"). This makes it so convenient to compose these build rules. And if you ever need to debug your build system, everything is very explicit in the generated ninja file. We don't have a ton of these containers so it has worked well enough for us because the apt layer changes rarely, and the layer above it is small/fast. The main thing we get (that you don't get from vanilla docker/apt) is lockfiles and reproducibility.