On Sun, Dec 28, 2025 at 15:30:04 +0100, Jose E. Marchesi via Gcc wrote:
>
> >> Not sure how relevant this is for C++, but soon we will be sending a
> >> proposal to Automake for supporting non-optional dependencies and thus
> >> having native support for Algol 68, Fortran and Go modules/packages.
> >
> > Highly relevant. Actually it would also concern Ada, Haskell, etc. because
> > all
> > “compiles into an intermediate binary representation” module systems are
> > alike.
> > It’s just that people expect more from C++ and C++ indeed has a far more
> > complex context.
>
> We basically borrowed the Go model for Algol 68 modules. In this model,
> the "exports" data that conforms the interface of a module gets
> generated in a section .a68_exports, or .go_exports for Go. Of course
> the Go exports and Algol 68 exports are very different, in both encoding
> and actual contents, but the overall model is more or less the same.
>
> In a nutshell, when you compile an Algol 68 compilation unit that
> contains a module named Foo:
>
> $ ga68 -c foo.a68
>
> it results in a foo.o with the compiled object code, plus the exports
> data in .a68_exports. If you then compile another file, bar.a68, that
> uses the module defined in foo (via "access Foo"), you then do:
>
> $ ga68 -c bar.a68
>
> When the compiler sees the "access Foo" in bar.a68, it searches for
> exports info for a module of that name. It does so by looking, in this
> order:
>
> - For a file foo.m68 that contains just exports info.
> - For a file libfoo.so that might have an .a68_exports section.
> - For a file libfoo.a whose objects might have .a68_exports sections.
> - For a file foo.o which might have an .a68_exports section.
>
> Both -I and -L paths are searched this way. The name mapping can be
> altered by passing "module maps" to the compiler via -fmodules-map and
> -fmodules-map-file options.
>
> If the exports are found then they are searched for Foo's exports. If
> these are not found an error is issued.
>
> Exports data is concatenable, so linkers just DTRT when combining
> several object files into archives or DSOs.
>
> Using separated .m68 files should not be necessary in most cases. An
> Algol 68 library distributed in the form of one or more DSOs will be
> shipping all the necessary exports withing itself.
Just from a glance, I think CMake would handle this exactly like it
handles C++ modules: try to bypass all search paths and make it
explicit. Basically, generate `-fmodules-map-file=` contents during the
build to answer all questions based on "scan" results (see below).
Not that CMake supports Algol68 (yet? ;) ).
> > For example, Fortran modules’ intermediate representation also has the
> > incompatibility problem, but far as I know Fortran users just manually
> > manage
> > them. A Fortran user told me he just makes a directory to place the
> > intermediate files for every set of compilation flags he needs. Whereas in
> > C++,
> > people expect package managers/build systems to automatically solve this
> > problem.
> >
>
> I'm just finished with the implementation of the modules system itself
> in ga68 (but for a few remaining nits) and I haven't had a chance yet to
> reflect about (or even become aware of) most of these problems, so I
> don't know how many of these will impact Algol 68, but it would be nice
> to come with a solution/strategy/automake that would also work for the
> other languages as well. It would be difficult to come with something
> that works for Algol 68 but will wont for Go, and most likely Fortran as
> well, but in the case of C++ I am not that sure, because AFAIK C++
> modules are more like an intra-compiler optimization, i.e. are they
> supposed to survive a final link and be distributed?. I obviously have
> to get a clue, and I will start with the links provided in this thread.
Discussed in the sibling reply.
> The main limitation in Automake is that it currently supports automatic
> dependency tracking, but only as an optimization for re-compilations,
> i.e. given a list of sources foo.c, bar.c, baz.c, you should be able to
> compile them in any order into foo.o, bar.o and baz.o (the header system
> of C assures that.) If you have automatic dependency tracking enabled,
> that compilation also extracts dependency info (via -M flags) that can
> then be used in further re-compilations to optimize.
There are, fundamentally, two different kinds of dependencies at play
here:
discovered dependencies
These are the typical dependencies reported via `-MF` and
friends. While they depend on contents, the files are generally
assumed to be extant at the point of compilation (i.e.,
generated headers are handled either as "written with the build
system" or "baked in the dependency graph by hand")
dynamic dependencies
These are modules (Fortran, C++, Algol, etc.) where the build
graph needs to first inspect for the dependency and *order work*
based on those contents so that derived information is available
as needed. This is *possible* in POSIX Make, but requires a
static 2-level recursion mechanism (AFAIK; GNU Make may have
features that allow it with a global graph).
P1689 used to have discovered dependencies listed as well, but it was
removed to make it easier for adoption.
> Our modules are different, of course.
>
> Thinking quickly, the compiler would need to be invoked first with all
> involved source files plus context (in -I or -L paths) to generate
> _complete_ dependency info, taking into account the whole picture, but
> no exports nor object code yet:
>
> $ ga68 -MD foo.a68 bar.a68 ... -I.. -I.. -I..
This could be replaced with a "scan" step which generates information
about the dynamic dependencies of the source. Build systems could then
use that to integrate with their generated build graph (e.g., to place
outputs somewhere and hook them up across directories). If you do go
this route, please consider using P1689 as the reporting format for such
dependencies. Some of the "that's weird for C++" comes from the
consideration for future Fortran support (CMake uses it internally for
its Fortran scanning).
Note that you want the scan step itself to also report `-MF`-style
dependencies for the files it reads so that it can be rerun as
necessary.
Scanning can be per-source or batched; P1689 supports both styles of
reporting. CMake only implements per-source. Batched is something I'd
like to do eventually so that performance behaviors of each can be
quantified. My gut is that per-source is better for
incremental/developer builds while batched is better for CI (at least
where there isn't a caching tool for scanning involved).
> This will also detect and report invalid configurations with circular
> dependencies etc. The resulting dependency information, maybe in the
> form of phony make rules with fill-able actions, would then be consumed
> and used by Automake in order to arrange the rules for building each
> object, then different primaries like executables, libraries, libtool
> libraries, etc.
This corresponds to the "collate" step described in CMake's
documentation.
> Both Autoconf and Automake already have Algol 68 support, but since the
> later doesn't know about Algol 68 modules yet, we are forced to handle
> dependencies by hand, which is a PITA. For example:
>
> AM_A68FLAGS = -fcheck=nil -Whidden-declarations=prelude
>
> bin_PROGRAMS = godcc
> godcc_SOURCES = utils.a68 argp.a68 http.a68 json.a68 ce.a68 \
> list.a68 compile.a68 format.a68 globals.a68 \
> godcc.a68
>
> # Dependencies.
> # In the future will be generated by ga68.
>
> utils.o: globals.o utils.a68
> argp.o: utils.o argp.a68
> http.o: utils.o http.a68
> json.o: utils.o json.a68
> ce.o: globals.o http.o json.o ce.a68
> list.o: globals.o ce.o list.a68
> compile.o: globals.o ce.o compile.a68
> format.o: globals.o ce.o format.a68
> godcc.o: compile.o list.o format.o godcc.a68
Fun. At least it is better than Intel Fortran documenting the "rerun the
build until it succeeds" strategy for its module compilation :) .
> Things get worse with Algol 68 projects using libtool:
>
> AM_A68FLAGS = -std=gnu68 -fcheck=nil \
> -fmodules-map-file=$(srcdir)/Modules.map
>
> lib_LTLIBRARIES = libgramp.la
> libgramp_la_SOURCES = lg-error.a68 \
> lg-symbol.a68 \
> lg-word.a68 \
> lg-alphabet.a68 \
> lg-grammar.a68 \
> lg-mgrammar.a68 \
> lg-vwgrammar.a68 \
> lg-language.a68 \
> lg-automata.a68 \
> lg-graml.a68 \
> lg-gramvm.a68 \
> libgramp.a68
> libgramp_la_A68FLAGS = $(AM_A68FLAGS) -I../common
> libgramp_la_LIBADD = ../common/libgrampcommon.la
>
> Where for each .a68 file both PIC and non-pic files are built, with
> names like:
>
> libgramp_la-lg-word.o (non-PIC version)
> .libs/libgramp_la-lg-word.o (PIC version)
>
> where both objects contain exactly the same .a68_exports sections.
>
> This is why we want to have built-in Automake support, sooner than
> later. Handling these dependencies manually in Makefile.am is just
> crazy.
It looks like a scan/collate strategy would work here. I presented this
paper to ISO in 2019 describing the overall strategy CMake uses for
Fortran:
https://mathstuf.fedorapeople.org/fortran-modules/fortran-modules.html
The information is in the CMake docs linked earlier, but this has
pictures :) .
> > This could be interesting! I’m not familiar with Automake’s evolution
> > process.
> > Where would you be sending the proposal, Automake mailing list?
>
> Automake changes are discussed in the Automake mailing list,
> [email protected]. CCing this list may make sense as well so everyone is
> in the loop.
Hmm. I believe I am on autoconf@, not automake@. Time to go subscribe
there too…
Thanks,
--Ben