...a Makefile and vim (or emacs, or even nano, I'm not going to judge your kink) are fine. If they are not fine, then C is probably not the right language for the project.
This is called gatekeeping and it’s not helpful.
It’s fine to write C in vim/emacs/nano if you want.
It’s fine to write C in Visual Studio Code if you want.
It’s fine to write C in CLion if you want.
There is nothing wrong with any approach and no need to gatekeeper an entire programming language based on editor preference.
We always talk about how good engineers "choose the right tool for the job". I don't think expressing an opinion on what the right kind of job is for a particular tool should be out of bounds. (Setting aside whether the opinion is correct or not.)
If someone wants to do that anyway, I am going to be perplexed that C was the language choice rather than golang or rust, and perhaps worried about C being a footgun WRT security, but whatever.
What a bad faith interpretation.
It's advice based on the poster's experience, stated with both qualification and good humor. You are free to ignore it. I appreciated it.
If you work on a C project, you need to be ready to have to edit source code, at least minimally, from an environment that doesn't have GUI. You will have to interact with pagers, man readers, readline a lot -- it's a lot more convenient if your editor works in the same way as those tool.
You are just creating unnecessary problems when you use VSCode or a similar editor. And I cannot think about a single benefit that would come from using it. Beside other things, it's just a crappy text editor... the only thing that's going on for it is that it's a Web browser application... which is kinda worthless when it comes to a typical C project.
So, let's say we have a project. For whatever technical (or social, historical, religious...) reason, C is chosen as the right language.
Why does that imply that the programmers on that project should therefore write that C code in a terminal, with no linter, code formatter, static analysis, test runner, etc.?
There are, of course, always exceptions.
Teams can use a combination of different IDEs and run the tools at different levels or their pipelines, which necessitate that they be automatable (and not GUI only).
If you don't have the exact machine any more, then it's "Good luck getting the toolchain and build environment from the vendor!"
Docker has saved my bacon at least twice in this manner.
It can be a vm, even a chroot.
Also the compatibility questions and docker "opacity" come to mind.
A QEMU VM sounds like a better propositions
I write a lot of pure Win32 C, and I don't even need a Makefile for the majority of my projects because they're just compiled as a single file and quickly enough that I don't bother with anything more than necessary.
IMHO the "tool fetish" that a lot of (mostly newer) programmers seem to have is an artifact of a mentality that favours complexity and novelty over simplicity and efficiency. They will waste tons of time and resources configuring and debugging (often with little understanding) huge complex monstrosities of "development environments", and end up feeling more productive, but in reality aren't.
This article just further confirms what I'd already expected with the word "modern".
I think simplicity is underrated too, but I hope you'll agree that your single c file use case is rather niche.
Also, every new job I've had, I've watched my coworkers spend like a week trying to figure out how to make the IDE work with whatever environment, which then changes later... I just skip that.
But in all seriousness. Using a debugger can be useful, and even though I've given it a try numerous times, I mostly avoid it because it doesn't fit the way I think.
Debugging is about searching for the source of the problem. With print debugging I'm always leaving behind breadcrumbs which I can inspect all at once at the end. If I'm going down a wrong path I delete the wrong ones and add new ones until I find the issue.
With a debugger I have to mouse around, put breakpoints, run the code, inspect where I'm at, step through, decide that this breakpoint is useless, and have to have all this state in my mind. If I get lost, I have to start from scratch.
Print debugging matches my way of thinking much more.
Now, if they were larping as PC/Mac one... (or certain groups of Unix weenies)
Imagine developing an embedded device for medical or aerospace hardware : you are going to provide lost of effort into testing. You are going to work with teams of people with varied abilities and experience : it's going to get a little bureaucratic, there are going to have rules and guidelines. Enforcing those with tools remove part of the friction, if done well.
Most avionics these days are done in Simulink, and then you hit the Autocode button.
It's for C/C++ as author says, not just for C. And even if you're using Qt and write mostly QML you still need some C++ and it's much easier with code completion. I'd rather use VSCode than Qt Creator for that and I'm certainly not going back to vim.
I also write Free Pascal using Lazarus, an IDE which actually understands the language at a deep level.
The experience writing the latter is way *WAY* better than the former and my opinion to that isn't "just write more in Free Pascal" but "i want an IDE for C that is at least as good as Lazarus is for Free Pascal".
I want a C IDE that, among others:
1. Can understand the code so that when i, e.g., want to rename the identifier "foo" it doesn't try to rename it as a keyword but knows if it refers to a local variable, global variable or struct member and only update references to that.
2. Can understand the code so that if i use a symbol defined in a header file that the current module doesn't include by itself it can put it in the includes section automatically (or at least ask me to) instead of waiting for the compiler to fail. If there is some conflict (e.g. because the symbol definition depends on macros or whatever) it should be able to understand that.
3. Can understand the code so that it can move structs, functions, etc around in modules and update header files and their uses in other modules automatically.
4. Can understand the code so that it can convert a struct defined in a header file to an opaque struct and vice versa, for the former being able to tell me which existing code would break and offer fixes (e.g. automatically creating accessor functions for the members).
5. Can understand the code so that it can expand macros in place visually, allow me to edit the macros expanded in-place while actually modifying the header file (or wherever the macro is defined) and also quickly tell me where the macro is used in other places.
6. Can understand the code so that it can convert code to functions, expand functions inline (with any local variables being placed at a decent position and any conflicts handled - it can ask for example if it sees the inline code to use "for (i=1; i<10;" and the existing code also has a "int i" if it should reuse the "int i" or replace it with heuristics that provide decent defaults), add/remove arguments (with automatic updates wherever they are used), etc.
7. Can understand the code so that one can create queries like "replace all string literals to non-static functions whose name matches the '^foo_.*$' regex with calls to macro 'TXT' and the same string literal as the parameter to macro".
8. Has a debugger that works with all the basics (breakpoints, step in/through/out, watches, etc) and...
9. The debugger can modify a variable while the program is running.
10. The debugger can make function calls while the program is running.
11. The debugger can modify a function while the program is running (any new calls are done to that function).
12. The debugger can watch data over time and be able to display values in various means like various graph types.
13. The debugger can tell you where (in code) some use-after-free was originally allocated and then where it was freed and where it was used, all with nicely shown arrows, hyperlinks and graphics directly inside the editor instead of you having to manually parse (in your head) some callstack. Similar for other types of errors like accessing invalid pointers, array data out of range (should tell you both the range and the accessed index where that can be statically inferred), etc.
14. The debugger can put conditional breakpoints which:
14a. Can call functions defined in the program.
14b. Can check if the breakpoint comes from a specific callstack (e.g. break if function "foo" is called from "bar" but not from "baz").
14c. Can access local variables and arguments up the callstack (obviously the breakpoint will only break where that is possible).
15. There is a profiler that works and...
16. The profiler can create call-based profiles (think gprof) and statistics-based profiles (think perf).
17. The profiler can record the full callstack instead of just the function name.
18. The profiler can create full call diagrams (see Luke Stackwalker[0] as an example) as well as flamegraphs (see some perl script for perf i don't remember).
19. The profiler can keep track not only where* but also when* a sample was taken so it can create profile timelines. Actually just have it do everything i had a profiler i wrote some time ago for Free Pascal[1], including be able to be instrumented by the running program, filter via thread, call stack depth, etc.
20. The profiler can call functions in the profiled program to create additional profile (e.g. if you want to count how many files are opened or how many rays your raytracer is casting or whatever so you can use the full profiler functionality instead of hacking up your own)
21. There is a static analysis tool that works and...
22. ...works like whatever Xcode's integration with Clang's analyzer is, i do not have much experience with those but i remember using it a couple of times years ago and thinking it was neat. I haven't seen any other graphical integration of a code analysis tool, pretty much every other analyzer feels like compiler warnings++. More of the graphical approach and less of the warnings++ approach please.
23. There is some form of project management / build tool / whatever that the IDE uses and...
24. The IDE can let you setup various "configurations" for build options (including preprocessor defines, which files/objects/libraries are to be included, etc) that can be mixed and matched at will (e.g. a "lightweight" and "full" configuration set could be mixed with a "win32" and "linux" configuration set with the latter relying on the "unix" configuration and none of those would need you to duplicate any information).
25. The IDE can know about libraries, be able to locate them as well as place them in appropriate locations when you are building a library. Libraries should be able to be built as both statically and dynamically linked if that is needed. If a library A relies on another library B and a program uses library A it should not need to also specify library B too - in the exceptional case where that is needed (e.g. there are alternative versions of library B) it should allow that but it should not be necessary.
26. Setting up the above should not need to be done via arcane text files but via a nice GUI that doesn't hate the user - e.g. using a library should not need you to type it's name but allow you to check next to its name in a listbox with checkboxes (with name filtering and categories). It should also know about different system libraries for the same thing (e.g. OpenGL in Windows, Linux and Mac is accessed via different ways). This should be configurable, not hardcoded - a custom library should also be able to use that functionality. Note that all of this can be stored in text files (e.g. for easier VCS support) but not needed.
27. More of a #26b but i think it deserves its own point: in addition the IDE should have deep knowledge of what a library offers and like #2 if it knows a symbol being offered by a library it should automatically add it to your project's requirements.
28. Again more of a #27b but also a #2b: it should be able to clean up any unused stuff (either automatic or explicitly). In total i should, e.g, be able to use OpenGL by typing "gl", pressing the auto-complete key (ctrl+space or whatever) and have the IDE suggest all the "gl" prefixed functions like "glClear", then once i select "glClear" (or whatever), the IDE adds the #include <GL/gl.h> header and the libgl library in the dependencies. If i press Ctrl-Z to undo that, the header files and library dependencies (if added) will be removed. If i press Ctrl+S to save or some other key to cleanup the project (depending on the configuration), any unused headers and libraries should be removed.
I could write for more but basically i'd like an IDE (i haven't touched topics like VCS support and how i'd like to be able to see and use different version of the code from inside the IDE - like e.g. go back in time to a different function while the debugger is running - or anything that has to do with GUIs) that is smart and helps me get rid of all the manual drudge,
(also FWIW little of the above is provided by Lazarus and i'd also like Lazarus to do all that where it makes sense but it still does more than pretty much any C IDE i've used)
I wrote some simple C a while ago and my go to IDE (which ostensibly says it supports C/C++ out of the box) wasn’t working, and since this was just a dabble, I was really not interested in fighting the IDE.
And, mind, I haven’t touched C in 20 years.
So I fired up Xcode. And, boy, that was easy. For my silly thing, I fat fingered and left thumbed my way to success.
No doubt Xcode has its critics and limits, but for my 3 hour project, I got to focus on my code and not the IDE.
I have another, more substantial pure C, not Mac specific, project I’m thinking of starting, and I’ll go with Xcode until it fails me.
Really. I'm not kidding. It's all there. Not for everybody but /clearly/ very much for you. (Unless you move the incredibly complex goalpost you've set up here).
It has a learning curve to configure and to use but if the above 28 is what you want as you say you /can/ have them all.
All of them. For real. Working. It's on you now.
29. vim or emacs keybindings if you want that.
> 11. The debugger can modify a function while the program is running (any new calls are done to that function).
Another words: hot code reloading / swapping [1].
And I would add:
> You record a failure once, then debug the recording, deterministically, as many times as you want. The same execution is replayed every time. [2]
[1] e.g. https://github.com/ddovod/jet-live, https://github.com/crosire/blink, https://github.com/ziglang/zig/issues/68
Real developers only code in Assembly with ed.
Sorry buddy, you might believe Makefiles are fine only if you are not aware of the most basic requirements of a build system. CMake does stuff like running sanity checks on libraries, configure them for you with minimal effort, and even add platform-specific configuration easily. Did you know that cmake started as a Makefile generator? Why do you think people need that?
Makefiles alone were never enough, as the development of tools such as the autotools family demonstrated decades ago. Claiming otherwise just seems naive flexing from someone who has no real world experience whatsoever.
I like make myself, but I’m honest enough to acknowledge that the whole autotools suite (autoconf/automake) was born to, essentially, generate makefiles.
Which is not 100% make’s complexity’s fault though… the toolchains have their own complexity (even more so when a project must build across platforms)…
None of this solves C's only REAL problem (in my opinion) which is the lack of dependency management. Most everything else can be done with a makefile and a half decent editor. No need to step up into vscode if you don't want to. Clang LSPs are basically everywhere and just fine.
Care to hear about our lord and saviour Meson?
Both of your quoted problems are mutually incompatible: dependency management isn't the job of the compiler, it's a job for the build or host system. If you want to keep writing makefiles, be prepared to write your own `wget` and `git` invocations to download subprojects.
Meanwhile, Meson solves the dependency management problem in a way that makes both developers and system integrators/distributions happy. It forces you to make a project that doesn't have broken inter-file or header dependency chains and cleans up all the clutter and cruft of a makefile written for any non-trivial project, while making it trivial to integrate other meson projects into your build, let other people integrate your project into theirs, and provides all of the toggles and environment variables distribution developers need to package your library properly. You can really have your cake and eat it too.
I'd juxtapose these two sentences.
As I understand things. Those who know Nix swear by it. You can declare a development environment which will provide the toolchain and the libraries you need to build your software.
Some things do seem inelegant about Docker containers. e.g. Building the images with Dockerfiles feels fragile. Running containers means high friction to accessing the build environment from the host machine.
Those downsides aside, AFAIU the VSCode devcontainers aim to provide that "wow it just works" experience that the Nix people love, without having to pay the steep learning curve of learning Nix.
I thought this too for a long time, but the more I'm exposed to languages with "proper" dependency management the more I appreciate the C way of just copying external library sources into the project (and I only consider libraries which make this easy, e.g. if they come with their own complex build system files they already lost - just give me a bunch of headers and source files, and maybe a readme with a list of configuration defines).
Those who tried it and swear by it.
Those who tried it and swear at it along with words like 'never again'.
For several decades, I have never compiled any C project otherwise than with auto-generated dependencies between files, which are generated by the compiler and used by make.
I never write by hand dependencies for a makefile. (I also never write by hand the list of source files, but I let make search for them and choose the appropriate compiler)
It is true that many open-source projects have horrible makefiles, but I have never understood why anyone has written such makefiles, when the GNU make manual explains the right way and that explanation already existed at least a quarter of a century ago.
It's obviously not infinitely scaleable because you could end up with name conflicts for object files, or some modules might require custom build settings. But it works well enough to nest my own projects 3-4 layers deep without issue.
Of course Git kinda sucks and isn't good at hosting toolchains. So I'm putting a heavy emphasis on "should".
I thought devcontainers were merely a way of telling VSCode to host your dev environment in a Docker container. I'm confused about what distinction you are drawing here.
Right, which is gross. It gets worse when you start talking about using them practically in an enterprise-ish environment. There, they end up being a less effective Xen-style programming interface. It's too bloated for most cases. The distinction I make is building with docker (for cross compilation or whatever) vs hosting your entire dev environment in a container.
The tool chains for ARM GCC on windows are a real pain to path correctly for multiple systems. Not to mention the same issue for OpenOCD, Debug, etc.
Bazel solves this really nicely, I know some people have strong opinions on it but I cannot recommend it enough
Docker vs. devcontainers: You got my point. It isn't about trying to force people to use devcontainers and vscode, it's about maintaining and sharing a development environment. You could just open a shell in this container and swap the base image out for whatever you need (also pointed out in the article). I myself also don't use vscode devcontainers but just exec into the running container and, e.g., use docker compose (or podman or whatever works best) :)
EDIT: The reason for showcasing devcontainers is that you get an IDE with code completion, format on save and all the other goodies "for free", whereas any plain docker or nix setup requires that you do this on your own. In my career, I've seen way too many people editing code in notepad or notepad++, making tons of mistakes that can be avoided by whatever IDE. I'm not saying vscode is best, I'm saying right now it is the easiest IDE to set up.
It is recommended at work now, where I also can use "the old way" which is a series of Makefiles. I've never had a problem with the Makefiles, but they've been hand-tuned for a few decades, and errors would be reported immediately by the build server. They check if you have the right compilers and then just work. Contrast this with Docker, where you always have the right compilers and they only work sometimes.
While using Docker, if even one source file changes while the compilation is happening, the VM will simply hang and refuse to compile or accept any break keystroke. Outside of Docker, a file change sometimes works without error and sometimes you'll cause a minor problem that a new iteration of "make <whatever>" fixes in a few seconds. Worst case? "make clean", "make <whatever>", no reboot required.
After force quitting the Docker-based build, I try "machine stop" and "machine start" but often encounter errors and need to reboot my machine and/or reinstall Docker, such as this gem from last week, "No connection could be made because the target machine actively refused it."
I don't know if Docker has an equivalent to "make extraclean" or "make distclean" or "sudo cut the bullshit && let me use you again", so I'm already uncomfortable restarting a build after rebooting/reinstalling. (That you need to manually delete container directories after uninstalling is also troubling.) I know some data are stored in the container in a mixed-persistence way that uses the existing filesystem. It's confusing to me why some generated output (.pdb, .exe, .dll) are accessible outside the container while others (.o) are evidently not used if you decide later to rebuild without Docker. Thus, the difference when switching is often recompiling 5 files in a few seconds vs 500+ in a few minutes, so there's a short-term incentive to try to keep using Docker, where I then waste time trying to solve Docker instead of just recompiling with the old method. Any semblance of "Flow" I have after getting a Docker error gets annihilated.
If our local container server goes down (it's not often but has happened), I am unable to build because some of our dependencies aren't FOSS/widely available.
I've had issues related to services, prerequisites, permissions, mounts, certificates, massive network login delays... When I hear the constant praise for containers, it gives me pangs of imposter syndrome, because my experience has been awful and mentally taxing. I find I'm constantly fixing tools instead of using them.
I can't necessarily blame Docker, but twice in the past 3 years I've needed to install a fresh OS within a week after installing it because of the problems above.
Docker's main selling point? It fixes a problem we don't really have. Every developer at my company has essentially the same computer---an overpowered Ryzen/Quadro combo in a black tower with a small collection of preinstalled essentials like MSVC bintools, VCS software, network drives preconfigured---unless they specifically request another for build purposes, i.e. Mac or Sun. In the latter case, that developer (or small group of developers) is in charge of builds on that host OS. In the one place it matters, we have one version of a compiler expected in a specific directory, and this is well-documented and changes once every few years.
It's possible I completely misunderstand Docker or have a misconfigured system (whatever THAT means, I thought the whole point was to eliminate problems caused by local customizations), but for anyone to tout it as fool-proof would mean they have severely underestimated the technical (in)abilities of fools.
Docker, to me, often feels like bringing in and using a CNC machine when all I usually need is a sharpening stone and chisel.
-----
Also, I would be quite uncomfortable using GitHub Actions to compile C for a microcontroller. There's simply too long of a delay between changing a config YAML and getting an error, fixing it, waiting for the next error. Plus, despite the "low overhead" of Docker, if you do this long enough on GitHub (especially with a non-Linux host OS) you will run into server fees.
And after you set up a GitHub Action, are you writing in VS Code, waiting for the remote computer to compile, downloading the artifact, syncing that with VS Code so you can debug, flashing to the target with your own set of (locally installed) tools, and finally debugging?
Contrast that with a local install... Edit. Save. "make", "make flash", gdb. In 20% of the time, with no server costs.
Every single one of the steps here, though they may seem excessive provide immense value, not at first, but as the project grows, matures, and needs to exist for decades.
I am rather surprised at all the negativity here. I assume that it's either few of the people commenting have worked on embedded systems, or the few that do do not have an appreciation for maintaining a large project for the long term.
Attitudes like I'm seeing expressed here are reinforcing my desire to get away from embedded, or at last work with people that don't wish to stay stuck in the stone age.
I'm not intending to argue here, just surprised and a bit sad at the reactions, and happy that some people, at least at memfault share my values.
I hate to break it to you, but there’s the same problems on the SWE side too. The problem is never the stack, it’s the culture. If you’re tired of embedded, that’s fine, but remember to ask culture questions for your next position.
> remember to ask culture questions for your next position.
I will definitely take that under advisement. Any places you recommend I look into?
Disagree. I had to learn make for my first job. I found two ways to manage dependencies that are probably the most common, and the limitations. There really isn't a method that is perfect; I don't know if dependencies can be done (even outside of C) without occasionally rebuilding.
The methods that I've seen rely on using the compiler to generate .h dependencies via the preprocess command. This can be done with gcc / cygwin for cross compilation, or with the native compiler. I'll explain.
Method 1: the "better" method. Generate dependencies for your header files at make invocation, before compile step. This is more robust, but outside of very small projects, this can add a ton of time to the build.
Method 2: the faster, more common method. Generate dependencies as a side effect of the build. When you compile, you use the previous dependencies. A new dependency file is generated for next time.
They both have limitations. Method 2 can detect changes in the header files included from C files, but not headers included from H files. Method 2 can detect changes in header chain, but not all the time. I think it's the files that the headers include, those includes from that file don't get updated, but it's been a while since I've read the paper.
Anyway neither method is that hard. You use gcc to generate dependencies for each file, then combine into one file (or you may create
.d files, but for some reason a single file is common). That's it.Otherwise, add a step to compilation (some compilers may have a build in "byproduct" command). Every time you generate .c there's also .d. When you compile, you use the previous *.d file.
Yes you need to know make, and this is an intermediate-to-advanced make task, but this is true for any build system! Entry level is a user that usually just adapts a build from the engineer experienced with the build system.
I'd say if one has the sort of problems TFA says it addresses, then go for their solution. But if one does not see any problem a priori, then they should go for the simplest thing that could possibly work. One can always reconsider things later; but if one goes for that more sophisticated solution first, and then realize that it was actually not need, they've wasted their time - and perhaps introduced problems that would never had happened without.
Don't be fooled by "modern" anything - too often it introduces new problems while solving old problems. "Stone age" can also mean battle-tested and rock-solid solutions.
Nothing author describes is applicable for a large project or a long-term project or both.
He chose one of the worst editors available, and decided to incorporate it into environment setup. Any long-term or large project will have multiple people working on it. A fraction will find the crappy editor the author decided to use useful, but most will want something better. Most people with development experience will want to set up their environment in the way comfortable for them, this setup is asking you to jump through too many hoops, none of which bring any value.
Same goes for CMake -- I've worked on multiple large C projects. None used an off-the-shelf build tool like CMake. These tools are inadequate for large-scale projects. (But, lets give the author credit here, he never claimed that his project was large or long-term). Still, I had never found CMake to be useful, nor for small nor for big projects. Whenever I had to work on a project that used CMake, it was a major pain.
It is common to test stuff in containers during development. But, it's also more common the more lazy and less insightful the programmer is / intends to be. In my experience, better programmers usually set up their environment in such a way that they don't have to deal with the container nonsense, as it gets in the way of debugging and a bunch of other tools useful to interact / understand the program being tested.
So... maybe containers for a smoke test. But, if you plan on going long term... that's just not going to cut it. You need a proper environment where you have comfortable access to your program.
OK fine
> 09 Aug 2023
huh? the correct answer to this question should be "get a time machine and go back to 2003 or something. why people insist on carrying on with C in the face of its glaring issues is beyond me. I am not advocating for any specific language, because several other languages are around that might be a better fit. Rust, Zig, D, Nim, Go. just please let C die already.
nope. they all are. all depends on the use case. if your use case is "must do 100% of what C can do, no exceptions" then of course that leaves one option. but for many programmers the trade offs today are not in favor of C, and haven't been for some years.
Most people and companies cant delay new features and bug fixes for years with the excuse of “we’re rewriting the thing in $currently_trending_language”
by your definition, is that any language less than 50 years old?
Using Docker for setup a C development environment indicates that there are too many moving parts and the development environment is essentially very complex and that there's nothing that one do about it.
I wish more people would write such guides with the aim of reducing the development environment to its essentials that can be installed system-wide without being disruptive and thus not needing "Docker".
I use mingsys though . But theoretically it should be no trouble to change the compiler.
It is entirely optional, yet extremely useful.
Once.
Containers just enable you to do it more than once. And they leave you a plaintext napkin with the list of stuff you installed.
Make Zig Your C/C++ Build System
https://zig.news/kristoff/make-zig-your-c-c-build-system-28g...
Edit: this is a better demo (thanks jmull) https://andrewkelley.me/post/zig-cc-powerful-drop-in-replace...
https://andrewkelley.me/post/zig-cc-powerful-drop-in-replace...
since the container is executed in a VM, the I/O performance is significantly worse compared to a container
that is run natively. For compiled languages or for any process that creates a lot of files, this impact can be
significant since the overhead can be up to 100x of what you’re experiencing natively
Uhhhhhhh. I don't know what VM you're using... but if the I/O in your VM is 100x slower than the host, you can fix that. It should not take a minute and a half to write a single file.If you don't mount the intermediate directory, it'll be way faster. Alternatively, use OrbStack (I feel like I'm shilling this a lot recently, I'm just a happy user), and the problem goes away.
It could be that the VSCode remote container stuff also gets around this problem somewhat by working "in container", effectively opting out of the Mac->Docker FS stack issues (especially with file watching....).
The fact that we have an enumerable number of tools that write files and somehow ended up with a bunch of stuff built on file watching instead of signaling to a build daemon on file save/checkout is... It feels cleaner but in practice is a mess of downstream problems.
This article reminds me of an idea I once had: to write a memory manager/garbage collector for C. The challenge is to know the scope of a dynamically managed chunk of memory. Using that, the memory can be automatically garbage-collected, but this is difficult since C wasn't designed for this in mind.
I'm curious if anyone has any other experiences they can share.
If you work in the embedded space for a large OEM, ODM, or integration house, you won't see any of this ... you will see all commercial environments with big price tags for seats e.g. compilers will be ARMC, Green Hills, IAR; for DAST you'll see tools from Synopsys or Cadence (same for virtual prototyping); lots of ISO compliance tools from hard-to-remember small companies that do that for a living and charge a cool mil to setup and audit ... for CI/CD you will likely see GitLab if not home grown suites. Gnu tools are some of the worst. Containers? 30+ years at this with 10 major contracts with big companies (and dozens of smaller ones) and I've only seen one company use containers and that was for virtual prototyping. C environments move at 1/100th the speed of webdev because product cycles happen in 6-12 months: literally no time to bring up a new system that breaks everything (and for no real benefit).
Perhaps if you have a 20+ person team it's possible that a supplier charges "a cool mil", but I'm pretty skeptical. If you have a big team anything can be expensive. That would be like implying laptops cost $20k, because you bought for a whole department.
This shit is expensive, but not THAT expensive. Support is like a grand for lessons, $150-300/hr. However, these are very normal rates for any engineering labor.
> Containers? 30+ years at this with 10 major contracts with big companies (and dozens of smaller ones) and I've only seen one company use containers and that was for virtual prototyping.
There are _a_lot_ of small and medium size businesses working with embedded devices with limited on-site resources, and those will usually use external contractors. In that case containers are very useful to share an Automated Test environment. The alternative is a long back and forth or spending time on-site supporting teams with wildly varying skill levels.
Also as someone else mentioned when an old client asks for a new feature, the ability to take a snapshot in time is a huge time saver, rather than trying to replicate that build environment from scratch.
I said this elsewhere, but you'll get a pretty decent perf boost if you set your build intermediate directory outside your mounted volume.
Instead of rm -rf /var/lib/apt/lists/*, running `apt-get clean` as part of that layer will help more.
Wrapping docker commands with makefiles is sad times. Use https://magefile.org/ or a task runner instead. Try to avoid making it a scripting language dependency.
Installing ruby to install a build system when you're using cmake in the container is a bit bonkers, and the cause of the majority of the "bloat" here. I'd replace it with calls to cmake and unity directly. (And honestly, if you're going as far as using cmake, I'd ditch C altogether and usea subset of C++ with gtest)
But honestly, that's about all I can complain about. This is a neat, modern workflow.
For development, I actually don't like "repeatable build environments". I like using a different, continuously updated systems, I consider having a different environment for CI/CD and for development to be a good thing. It is one of the best way to test for compatibility problems, a kind of dogfooding.
Plus, it is nice working on a system you are comfortable with, and without the performance penalties of virtualization/containerization.
For releasing and testing however, containers are great.
The correct thing to do, imho, is check-in toolchains to version control.
[0] https://docs.unrealengine.com/5.0/en-US/setting-up-turnkey-f...
There's a bunch of problems that Docker solves for embedded toolchains that other C developers don't need to care about.
The author's example isn't really the best at illustrating this as he's just apt-get'ing things like gcc. There are plenty of cases where you'll need some specific version of gcc or some other weird tool like openocd or gdb that's hard to hunt down, or is required due to some weird development ephemera you found.
Docker makes it a bit easier to compile these requirements and hang on to them painlessly - even across a few years' time lag.
[1] https://wiki.alpinelinux.org/wiki/Running_glibc_programs
It's the old dilema everyone faces: pin every dependency version to guarantee you have a stable environment that will work in 20 years as long as things can still be downloaded? OR use the latest of everything, making a hugely unstable environment you have to keep fixing every few months, but at least get the latest "security patches" and other improvements (as well as new bugs)?
Though, I haven't used Debian-based distros in a while, but does apt actually serve really old versions of its packages? I vaguely seem to remember that you could realistically only `apt install` the last few versions of a package.
[1] https://askubuntu.com/questions/92019/how-to-install-specifi...
* XCode
* Makefile
That's really it. If I found myself needing to set up Docker... I'd just go outside and garden instead or something.
But if you meant that learning make is harder than using an IDE, then I agree. But why specifically XCode then? Lots of IDEs know C/C++ off the shelf.
Quasi because when your provisioning automation is doing things like `apt update` and grabbing the latest and greatest toolchains from third party repos, you're still producing an entirely unpredictable result.
I'll take updating a dependency on my dev machine every now and then (which could be largely eliminated if we had used something like conan) over maintaining my own docker image any day. You can also largely eliminate relying on system headers through --sysroot, which cmake supports.
Another issue we had, which this tutorial doesn't appear to address, is that these containers run as root, so files created in them on a mounted folder easily end up as owned by root. Which can create all sorts of mayhem. The only reliable solution I have seen is to dynamically change the container users uid and gid on login, but this often doesn't seem to get implemented.
- https://marketplace.visualstudio.com/items?itemName=ms-vscod...
- https://marketplace.visualstudio.com/items?itemName=ms-vscod...
This gives you a cmake-based IDE-like setup that works across Linux, macOS and Windows (and also allows to build and debug UI applications with the native OS APIs).
I don't have a need for Docker or any containers for that matter, unless the goal is to deploy in containers.
I would never touch VSCode with a ten feet pole. Same goes for CMake -- unless forced to by external circumstances, I will never use it.
Even if this environment was set up by someone else for me, I would've found it painfully difficult and uncomfortable to use... so, I don't know why would anyone else want this.
In C/C++ land? Nix is the virtual env. It’s the only sane choice for that as a user land stack.
> Docker is not platform-independent. Especially if you’re running a container on other CPU architectures, e.g., Apple ARM, you’ll notice that some things don’t run. We’ll see this later.
And it keeps going on about how Docker doesn't really solve the problem.
You are working against yourself, because eventually you will need to learn CMake and Make when you have to interop with other projects.
In day-to-day use, I find that there is very little that is actually modern about C or C++, and that's OK. Just focus on getting things done, and build up a working knowledge of all of the practical stupid things you have to do when working with C and C++ projects.
Like, it's bewildering that there are no cross-platform CMake recipes for building an app. Totally wild. But everyone slogs through this stupid nonsense while other platforms hand it to you on a platter. Just deal with it. There are other hills to die on that are wildly more important. Help others that struggle with arcane CMake b.s.
$ cat devenv.nix
{ pkgs, ... }: {
languages.c.enable = true;
packages = [ pkgs.ceedling pkgs.cmake ];
enterShell = ''
cmake --version
'';
pre-commit.hooks = {
clang-format.enable = true;
clang-tidy.enable = true;
};
}$ devenv shell
(Note that pkgs.ceedling has been recently added and will only hit the cache in 6-12h).
Using Zig is way easier, faster and more flexible.
I'm not talking about Zig as a language, but as a toolchain to compile C/C++ code.
Have my thanks.
1: https://docs.platformio.org/en/latest/platforms/creating_pla...