Skip to content

classicladder: give the headers include guards and their own dependencies - #4430

Merged
BsAtHome merged 1 commit into
LinuxCNC:masterfrom
grandixximo:classicladder-headers
Aug 21, 2026
Merged

classicladder: give the headers include guards and their own dependencies#4430
BsAtHome merged 1 commit into
LinuxCNC:masterfrom
grandixximo:classicladder-headers

Conversation

@grandixximo

Copy link
Copy Markdown
Contributor

30 of the 34 headers in hal/classicladder have no include guard, and 12 do not compile on their own. They name StrRung, StrStep, cairo_t or the gtk types without including whatever declares them, and build only because the file that included them had already got there.

Guards go in first, since including classicladder.h from twelve places without them redefines everything. Then the twelve name what they use.

The guards are CLASSICLADDER_<FILE>_H. The four that had one already used a leading underscore, which is reserved to the implementation, so they are renamed rather than left as a second convention in one directory.

Testing. All 34 compile standalone afterwards, one translation unit per header, where 22 did before. Built clean with --with-realtime=uspace, no errors and no warnings.

Is it worth having? @BsAtHome this is your point from #4425, that a header needing something should be the one to say so, applied to a subsystem you did not ask about. classicladder is old code nobody is working on, so if 34 files of churn reads as noise, say so and I will close it.

Independent of #4425, overlapping by one line, the <stdio.h> in files.h.

@rmu75

rmu75 commented Aug 21, 2026

Copy link
Copy Markdown
Collaborator

Adding to the noise: is there a reason not to use #pragma once? While not in any C/C++ standard it is probably even rarer for a compiler to not support that than non-two's complement int representation.

@BsAtHome

Copy link
Copy Markdown
Contributor

Include guards are de facto standard. No reason to to use pragmas. FWIW, pragmas should only be used when there is no other option.

@grandixximo

Copy link
Copy Markdown
Contributor Author

@rmu75 There is a concrete reason here beyond convention, so worth a measurement.

The build ships every exported header twice: src/Makefile:443 builds HEADERS, and each Submakefile cps its headers into include/. So src/emc/nml_intf/emcpos.h and include/emcpos.h are two files with identical content. Include guards dedupe by macro name and do not care. #pragma once dedupes by file identity and does.

Same two includes, one resolving to each copy:

gcc 14 clang 19
include guards clean clean
#pragma once clean error: redefinition of enumerator 'EMCPOSE_ERR_OK'

gcc saves pragma-once files by content hash, so it only breaks once the two copies drift. clang keys on the file entry, so identical bytes are not enough.

Not hypothetical: scanning the .d files of a build, three headers are already reached inside a single translation unit through both their src/ path and their include/ copy.

emcpos.h         emc/nml_intf/emc.o
rtapi.h          rtapi/uspace_rtapi_main.o
rtapi_stdint.h   rtapi/rtapi_pci.o, rtapi/uspace_rtapi_main.o

Under #pragma once those stop compiling with clang, and rip-and-test-clang is in CI.

For balance, guards do have the failure the pragma avoids: src/rtapi/examples/{fifo,semaphore,shmem}/common.h all use the guard COMMON_H, so whichever arrives first silently blanks the others. Latent rather than live, since no translation unit includes two of them, but it is why this PR had to invent 34 unique names by hand.

None of those headers are touched here, so this PR is unaffected either way.

@BsAtHome

Copy link
Copy Markdown
Contributor

There are now conflicts... Should be trivial to fix.

@rene-dev

Copy link
Copy Markdown
Member

keep in mind that classicladder is an external project, that has been copied into linuxcnc, and added hal stuff.
sometimes, upstream changes are pulled in. I think the last change was the move from gtk2 to gtk3.
https://sourceforge.net/projects/classicladder/

…cies

30 of the 34 headers in hal/classicladder have no include guard, and 12 do
not compile on their own.  They name StrRung, StrSection, StrSymbol, StrStep,
cairo_t or the gtk types without including whatever declares them, and build
only because the file that included them had already got there.

Guards go in first, since including classicladder.h from twelve places
without them redefines everything.  Then the twelve name what they use:
classicladder.h for the ladder types, sequential.h for StrStep and
StrTransition in drawing_sequential.h, <gtk/gtk.h> for the five _gtk headers,
<cairo.h> and <stddef.h> for drawing.h, and <stdio.h> for the FILE * in
files.h.

The guards are named CLASSICLADDER_<FILE>_H.  The four headers here that had
one already used a leading underscore, which is reserved to the
implementation, so they are renamed to match rather than left as a second
convention in the same directory.  Plain uppercase with no underscore is what
most of the tree uses; the prefix is because names like FILES_H, CALC_H and
GLOBAL_H are too general to leave unqualified.

All 34 compile standalone afterwards, one translation unit per header against
the userspace include path, where 22 did before.  Full build clean, no errors
and no warnings.

The stdio.h line in files.h is also in LinuxCNC#4425, which reaches it from the other
direction; the two overlap by that one line and nothing else.
@grandixximo
grandixximo force-pushed the classicladder-headers branch from 4ff0b0f to eb0ef6f Compare August 21, 2026 14:28
@rmu75

rmu75 commented Aug 21, 2026

Copy link
Copy Markdown
Collaborator

I wonder if this "same include file lives in 2 locations" doesn't also throw off functionality like "show all references" in tools like ccls / clangd.

@grandixximo

Copy link
Copy Markdown
Contributor Author

@rene-dev Fair, and the history backs it. The copy here is upstream v0.8.10 (December 2010) per HISTORY.txt, and the last sync in was Håvard's classicladder: Update <file> series in early 2022, applied file by file rather than merged. Upstream's last release is 0.9.113, 2020-01-04, with nothing since, so a future pull is a hand port rather than a merge. Since that series, 76 commits from 10 authors have touched the directory, all local.

Your point still lands on one part of this PR. Of the four guards I renamed, classicladder_gtk.h and protocol_modbus_defines.h got theirs in that 2022 series and protocol_modbus_master.h in the 2008 merge changes for classicladder ver 7.124, so those names are plausibly upstream's own. Renaming them diverges on exactly the files a port would touch, and buys only the reserved identifier fix. I can leave all four alone and add guards only to the 30 headers that have none, which is the part that makes them standalone. Say the word.

@rmu75 Probably, though I have not measured it, so I will not claim a specific behaviour. What is certain is that the include/ copies are build output: cp'd from src/ and ignored by include/.gitignore, sitting in the source tree. Which copy a translation unit sees depends on -I order, so an index built from real compile commands contains both, and a jump to definition can land in the generated copy, where an edit is silently undone by the next make. The tree also has no compile_commands.json generation, so most indexing here is on heuristics to begin with. Happy to measure what clangd actually does with such a pair if it is worth knowing.

If the whole thing reads as churn against a vendored tree, I am happy to close it.

@BsAtHome

Copy link
Copy Markdown
Contributor

If they resolve includes properly and actually understand the logic, then they should not. However, many tools make assumptions or do not understand it and will fail (miserably). If you must know, then you should test.

There is a reason why the includes are setup as they are, also the copying of several of them into the include/ directory. It is to align in-tree and out-of-tree builds. Many tools make assumptions that may or may not be valid. Users of the tools should be very aware of that.

@BsAtHome

BsAtHome commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

If the whole thing reads as churn against a vendored tree, I am happy to close it.

The classicladder in the LCNC tree is ancient and very misaligned with the upstream version. It would be a complete project to re-align the two. But it would only be worth it if there is a (significant) user base.

We have been patching and plugging holes in the LCNC copy for quite some time and this is no exception. Sure, you can also upstream the changes, but with the current misalignment I don't think it is worth the effort.

But keeping our copy working with our tree is more important. Therefore, update this in our tree? Yes.

@rmu75

rmu75 commented Aug 21, 2026

Copy link
Copy Markdown
Collaborator

@grandixximo don't bother, I know that it doesn't work 100% correctly. I usually generate compile_commands with "build ear" bear [1], and that records accurate compiler invocations for each translation unit. Depending on what code you look at "go to..." sometimes jumps in the /include dir and sometimes in the src.

CCLS and clangd are actually using clang so behaviour will be like clang (in other words, symbols declared via including /include/file.hh ... are treated as different from symbols declared by including file.hh in the source dir)

At least now I know what's going on...

[1] https://github.com/rizsotto/Bear

@BsAtHome

Copy link
Copy Markdown
Contributor

Depending on what code you look at "go to..." sometimes jumps in the /include dir and sometimes in the src.

That is because the compile is showing you the include file it used. That is always the one from the include/ directory when it is a "foreign" (for out-of-tree build exportable) include for the code being compiled and it used <>. When it is a local include then "" is used. Yes, it can be confusing once in a while, but the alternative, inconsistent includes, is worse.

@BsAtHome
BsAtHome merged commit 7a29eb2 into LinuxCNC:master Aug 21, 2026
17 checks passed
@grandixximo
grandixximo deleted the classicladder-headers branch August 22, 2026 07:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants