gm2
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Partial success building the trunk on MSYS2/Windows platform


From: Benjamin Kowarsch
Subject: Re: Partial success building the trunk on MSYS2/Windows platform
Date: Sun, 6 Nov 2022 19:24:02 +0900

Hi Runar,

On Sun, 6 Nov 2022 at 03:01, Runar Tenfjord <runar.tenfjord@gmail.com> wrote:

I agree with your arguments for writing portable code, but
for embedded software use there is a need to make the library
configurable in order to remove features as you are often struggling
to shoehorn your code into a very small flash size.

So, what's the "but"? Writing portable code in the manner I described does not mean libraries aren't configurable, does not mean you can't remove features that aren't needed, does not mean the libraries become larger. Quite the opposite is the case. It leads to better configurability, better selectivity and ultimately smaller size.
 

If you look at some embedded library code bases there is
a large number of preprocessor flags to enable/disable features
in order to adapt the code size to the available flash size.
For example you want to remove the floating point part of
the printf function as this is easily 20k of code.
So practical issues leads to a cluttered code base in terms
of readability in the embedded space.

What you refer to are examples of combining preprocessing and compiling into a single step, which is the very thing I criticised. Instead, I advocated an alternative approach. You can't use the very same approach I criticised to reason about the alternative I proposed.

There are essentially two ways to produce multiple bespoke outputs from a general input.

(1) one sole input file that contains all the variants and a means to switch them on and off.

(2) multiple input files that contain one variant each and a means to generate them from a generic template.

The first approach is the common approach used by most language environments that include preprocessors. This is the one that produces a lot of clutter, not surprisingly. The second approach is not very commonly found, whatever clutter it may produce is moved from the bespoke source code into generic template code and thus out of sight for all downstream development work.

It isn't practical issues that lead to clutter, but the choice of approach.


This is then limited to the library code, so if
we use standardize library interfaces, it should still be portable.

Not if you are using BITSET, CARDINALnn and INTEGERnn types.

 
Embedded software in C can be portable if POSIX is used,
even if the underlying C library is very specialized.

C is a different story. Even though there are a few GNU only features in GCC, these aren't all that common, and due to GCC's ubiquity, when any such non-standard feature became widespread, it was eventually adopted into the next C standard. Even so, C's built-in preprocessor represents approach #1 above and that leads to precisely the kind of clutter and difficult to maintain code I criticised.


I guess the ISO library the closest to POSIX,

The ISO Modula-2 IO library is about as far removed from POSIX as it gets. Sure, any implementation thereof on a POSIX based platform will ultimately call the POSIX interfaces underneath, but there is a lot of unnecessary layering piled on top.

And if you want to avoid unnecessary bloat when targeting embedded platforms, you certainly don't want to use the ISO IO library.

By contrast, if you build a small IO library you will get cleaner code and a much smaller footprint. You can easily write multiple implementations thereof, one for each platform/environment you need to target. For example, one implementation on top of the Terminal and FileSystem libraries described in PIM, which would then work on all PIM compilers; one implementation calling POSIX interfaces which would then work on all POSIX platforms; and one calling Windows IO interfaces which would then work on Windows. Or if you target a specific embedded architecture, you implement the IO library with calls into the IO system interface of that architecture.

This approach is both lean and portable. Your build will always only contain the code for the specific platform it was built for.

For illustration, this is the approach we have taken with our simple IO library for our project.

https://github.com/m2sf/m2pp/blob/master/src/BasicFileIO.def

https://github.com/m2sf/m2pp/blob/master/src/BasicFileSys.def

The only thing that might be considered missing there are procedures in the file system library to read and write file creation/modification date-timestamps and permissions. Even if those are added, it will remain clean and nimble. And yet, it's all portable without clutter.

The different implementation variants are all compartmentalised into separate source files.

https://github.com/m2sf/m2pp/tree/master/src/posix


I could find there is not many active compilers left for
Modula-2 to target. It is mostly legacy code left (I could be wrong here)

That's still no argument in support of creating avoidable dependencies.

You spend all that time and effort on your project and then one fine day, for whatever reason, you may find it can no longer be built because of some dependency on somebody else's tool which broke or disappeared. Then what? Rewrite the whole thing? Or maybe there is some other tool to which the code can be adapted without having to do a complete rewrite.

Last but not least, when writing for portability and targeting multiple compilers/platforms, it is often the case that you discover bugs that would otherwise have remained undetected. And if you use the approach (#2) I proposed, you will probably spend more effort on architecture, source file hierarchy and build logic which will be beneficial in itself.

regards
benjamin

reply via email to

[Prev in Thread] Current Thread [Next in Thread]