Skip to content

posemath: split the header, move EmcPose, stop exporting gomath and sincos - #4411

Open
grandixximo wants to merge 3 commits into
LinuxCNC:masterfrom
grandixximo:posemath-split
Open

posemath: split the header, move EmcPose, stop exporting gomath and sincos#4411
grandixximo wants to merge 3 commits into
LinuxCNC:masterfrom
grandixximo:posemath-split

Conversation

@grandixximo

Copy link
Copy Markdown
Contributor

C, D and E of the plan agreed in #4387. Each commit stands alone.

Split the header by language. posemath.h declared the C++ classes and the C API in one file. The tree already has a shape for that, inifile.h/.hh and hal.h/.hh, so follow it: posemath.h keeps the C API and gains posemath_types.h for the types and constants, posemath.hh takes the classes, operators and copy templates and refuses to compile as C. posemath.h ends by including posemath.hh under __cplusplus, so C++ code including posemath.h still gets the classes and nothing out of tree changes. The gain is for C++ files wanting no more than PmCartesian, which can include posemath_types.h instead of four hundred lines of class declarations.

Declaration text moves verbatim. The one edit is PmAxis, an unnamed enum in C and a typedef of the C++ enum PM_AXIS in C++; the types come first now, so the enum takes its tag and one definition serves both.

Move EmcPose. emcpos.h held the type and emcpose.h the operations, and code wanting the type included the one that could not stand alone. Neither is an NML message. Both fold into src/libposemath/emcpose.h and emcpose.c moves with them. emcpos.h stays and includes the new header, so the 126 files that include it are untouched. The declarations gain an extern "C" block they never had.

Stop exporting gomath.h, gotypes.h and sincos.h. Five in-tree users between them, all kinematics, which include them by path instead. What remains installed is posemath.h plus its two headers, plus emcpose.h.

Testing. Build clean, no errors or warnings. runtests over blendmath, realtime-math, interp, ccomp and matrixkins: 91 run, 91 successful, 0 failed. All ten kinematics modules that link a posemath object load under halrun. Preprocessing posemath.h before and after, in C and in C++, gives the same declarations apart from that PmAxis line. libposemath.so.0 gains the fifteen emcPose entry points and liblinuxcnc.a loses them, and nothing in userspace calls them. Installed headers go from 38 to 37.

tpcomp.comp is filtered out of COMPS so nothing in tree compiles it, and it references emcpose.c by path; checked separately by setting TOPDIR and running halcompile on a copy.

@BsAtHome

Copy link
Copy Markdown
Contributor

Wouldn't it be more logical to fold sincos and gomath into the posemath header? It seems that we are including these from the local pool and then link to the .so library. But wouldn't it make more sense to isolate the library by public interface?

A few observations:

  • gomath.h includes stddef.h and float.h (so does gotype.h), which it probably shouldn't, and use rtapi instead. That also means some other mingling necessary.
  • both gomath.h and gotypes.h are only included a few places. The one in scorbot-kins.c seems misplaced at that too. The only real user of the go_* functions seems to be genserkins.c via genserfuncs.c. Then the include of the go* header in genserkins.h seems out of place, since genserkins.c does not use any of that.
  • the only place where pm_sincos() is used seems to be the tests/mathtest.c. So, why exactly are we including it? The whole sin() and cos() issue is moot because it is supported by the FPU and all our threads run in FP mode. There is a lot of places where sincos.o is linked, but I don't think it is used (anymore).

posemath.h declares two libraries in one file: a set of C++ classes with
their operators, and the C API that the real time side uses. The tree already
has a shape for that, in inifile.h and inifile.hh and in hal.h and hal.hh,
where the plain suffix is the C interface and the double one is the C++
interface, so follow it.

posemath.h keeps the C API and gains posemath_types.h, which holds the data
types and the constants. posemath.hh takes the classes, the operators and the
copy templates, and refuses to be compiled as C. posemath.h ends by including
posemath.hh under __cplusplus, so C++ code that includes posemath.h still
gets the classes and nothing out of tree changes. The two headers include
each other and the guards make either order safe.

What this buys is for C++ code: a file that wants no more than PmCartesian,
which is most of what reaches posemath through emcpos.h, can include
posemath_types.h and stop parsing four hundred lines of class declarations.
C compilers were never affected either way, since the classes have always sat
behind an ifdef.

Declaration text is moved verbatim, indentation included. The one edit is
PmAxis, which was an unnamed enum in C and a typedef of the C++ enum PM_AXIS
in C++; the types come first now, so the enum takes its tag and one
definition serves both languages.

Preprocessing posemath.h before and after, in C and in C++, gives the same
declarations in both languages, differing only in the order they appear and
in that PmAxis line. libposemath.so.0 exports the same 390 symbols.
EmcPose is a machine pose made of a PmCartesian and six more doubles, and
emcpose.c is arithmetic on it. Neither is an NML message, so neither belongs
in nml_intf. The split across two headers was historical as well: emcpos.h
held the type and emcpose.h the operations, and code that wanted the type
included the one that could not stand alone.

Fold both into src/libposemath/emcpose.h and move emcpose.c with it, into
libposemath rather than liblinuxcnc, which is the only in-tree caller anyway
by way of tpmod. emcpos.h stays where it is and includes the new header, so
the hundred and twenty odd files that include it are untouched and out of
tree code keeps compiling.

The declarations gain an extern "C" block. They had none, so a C++ caller
would have compiled and then failed to link; there is no such caller today.

libposemath.so.0 gains the fifteen emcPose entry points and liblinuxcnc.a
loses them. Nothing in userspace calls them.
The build copies gomath.h, gotypes.h and sincos.h into include/, which makes
them part of what LinuxCNC offers to code built against it. Nothing outside
libposemath needs them to be: gomath and gotypes are included by two files,
both part of genserkins, and sincos.h declares one function, a shim over
sin() and cos() that posemath itself calls.

Those users include them by path instead, which is what src/-relative quoted
includes are for, and the three headers come off the exported list. With the
split in place the installed pose math surface is posemath.h and the two
headers under it, plus emcpose.h, in one directory.

Porting genserkins and scorbot off gomath, so that the N-DOF matrix code can
move into posemath and gomath can go, is easier once the symbols are private.
scorbot is already off it: scorbot-kins.c includes gotypes.h and uses nothing
from it, so that include goes here.

gomath.h included rtapi_math.h with quotes, which was right while gomath.h
was itself exported and had to carry its siblings to include/.  It is a user
now, so it takes angle brackets, and it reaches the standard headers through
rtapi_math.h rather than around it.  gotypes.h wanted float.h for FLT_MAX and
DBL_MAX and gomath.h wanted stddef.h for NULL; rtapi_math.h includes float.h
and rtapi.h includes stddef.h, in both the kernel and the userspace branch.
@grandixximo

Copy link
Copy Markdown
Contributor Author

The scorbot include and the two rtapi_math ones are in, folded into the third commit. NULL still resolves in a kernel build, since rtapi.h includes stddef.h above the branch.

On sincos, before I touch it. _posemath.c calls pm_sincos() in five places and gomath.c in seven, and nm on the built genhexkins, scarakins, pumakins and tpmod shows the reference, which is why those nine modules carry sincos.o in their -objs. Does that change what you had in mind, or is the point underneath it still the same one? Because I think it survives the numbers: the function is two lines calling sin() and cos(), there is no native path left, and src/Makefile:917 sets -fno-builtin-sincos precisely so the compiler cannot fold the pair back into a libcall. If those twelve sites called sin() and cos() directly, is anything lost beyond the file, the nine -objs lines and the flag? If not, that is a change I would rather make on its own, since it alters what every one of those modules links.

On genserkins.h, genserkins.c really does not use anything go_. But genser_struct holds go_link links[], the prototypes take go_pose, go_real and go_screw, and PI_2 is GO_PI_2. Where would you have a header declare its own type dependencies, if not in the header?

On folding them into posemath.h: with scorbot's include gone, gomath and gotypes have two includers left, genserfuncs.c and genserkins.h. If they became part of the posemath interface, everyone who includes posemath.h for a PmCartesian would take the go_* type system with it. Would moving them out of libposemath and next to genserkins isolate the library in the sense you meant, rather than widening what it offers?

@grandixximo

Copy link
Copy Markdown
Contributor Author

Two corrections to my own numbers, before you spend any time on them.

pm_sincos() is called four times in _posemath.c, not five; I counted the #include. So eleven call sites, seven of them in gomath.c. And it is all nine modules that reference it, not the four I spot checked. Worth adding, since it is the actual evidence for the flag: rtlibposemath/_posemath.o has U sin, U cos and U pm_sincos, while the userspace libposemath/_posemath.o has U sincos, so -fno-builtin-sincos is reaching the realtime compile and not the library one.

And I conceded too much on genserkins.c. It does reach into gotypes.h: lines 48 and 49 #undef GO_REAL_EPSILON and redefine it to 1e-6, after including genserkins.h. So the dependency is not the header's alone.

@BsAtHome

Copy link
Copy Markdown
Contributor

The pm_sincos() function is pure legacy and obsolete. Replacing it with sin() and sin() allows the compiler to generate better code because we always have the FPU available. Therefore, it should be retired. Fine to have that in a separate PR.

When RT links with libposemath objects, then it must have been compiled with the RT compiler options. You cannot link libposemath.so to RT. There are generated objects in (grep -r rtlibnml):

src/objects/rtlibnml/posemath/sincos.d:objects/rtlibnml/posemath/sincos.o: libnml/posemath/sincos.c \
src/objects/rtlibnml/posemath/_posemath.d:objects/rtlibnml/posemath/_posemath.o: libnml/posemath/_posemath.c \
src/objects/rtlibnml/posemath/gomath.d:objects/rtlibnml/posemath/gomath.o: libnml/posemath/gomath.c \

We may want to make a parallel RT version of libposemathRT.a instead if that is necessary. I'm trying to wrap my head around the intertwined way things are built and how to untangle it...

@grandixximo

Copy link
Copy Markdown
Contributor Author

On the RT side I do not think there is anything to untangle: the tree already builds posemath twice, and has for a long time.

Every source named in a module's -objs list is compiled a second time by the RT rule, objects/rt%.o : %.c at src/Makefile:1338 for uspace and :1364 for the kernel build, with -DRTAPI and RTFLAGS. TORTOBJS at :1319 is what maps a module's libposemath/_posemath.o onto that RT copy. The userspace objects are a separate set and carry EXTRAFLAGS=-fPIC from libposemath/Submakefile:2, and libposemath.so.0 links only those.

Nothing realtime links the .so. It has two consumers in tree, both userspace: emc/task/Submakefile:33 and the standalone genserkins binary at emc/kinematics/Submakefile:21.

The rtlibnml paths your grep found are build artefacts rather than sources: .d files under src/objects/ left over from before #4387. My tree has the same ones, and no src/objects/rtlibposemath/ until it is rebuilt. They are harmless, since RTDEPS is derived from the current RTOBJS and a stale .d is never included, but they do make it read as if the move never happened.

A libposemathRT.a is a real option and I am happy to write it, but it would bundle the same objects the modules already name one at a time. The gain is deleting the nine duplicated -objs pairs, not correctness. Separate PR if you want it.

On pm_sincos(), agreed, and separate. Eleven call sites, four in _posemath.c and seven in gomath.c, plus tests/mathtest.c; then the file, the nine -objs lines and -fno-builtin-sincos at src/Makefile:917 all go with it.

On untangling, generally

You said you were trying to wrap your head around how the tree is built and how to untangle it. So did I, and guessing was not working, so I measured it. Below is a directory level dependency report for src/. I can add the generator as scripts/include-dep-report.py next to the check in #4375 if it is worth keeping, or leave it here as a one off.

The short version is that it is a lot less bad than it looks. There is exactly one cycle, it has 17 directories in it, and 18 of its 68 edges are a single #include each. rtapi is in that cycle because of one line.


The script resolves every #include in the tree the way the compiler does (quoted: includer's own directory, then -I., then the exported copy; angled: the exported copy), buckets each file into a directory at the granularity of SUBDIRS, and reports the edges between those buckets. The exported-header set is read out of SRCHEADERS in src/Makefile, so it follows what the build installs rather than a list of its own. Includes that resolve outside the tree are dropped, and .comp sources are not scanned.

Summary

  • 32 directories, 141 directory-level edges, 1198 include sites between directories.
  • One strongly connected component of 17 directories, 68 edges, 469 include sites. Every other directory is already acyclic.
  • 18 of those 68 cycle edges are a single #include.
  • 37 exported headers.

The emc core of the cycle

graph LR
  n0["emc/ini"]
  n1["emc/kinematics"]
  n2["emc/motion"]
  n3["emc/nml_intf"]
  n4["emc/pythonplugin"]
  n5["emc/rs274ngc"]
  n6["emc/tooldata"]
  n7["emc/tp"]
  n0 -->|1| n2
  n0 -->|17| n3
  n1 -->|9| n2
  n1 -->|4| n3
  n2 -->|1| n0
  n2 -->|3| n1
  n2 -->|3| n3
  n2 -->|7| n7
  n3 -->|3| n2
  n3 -->|4| n5
  n3 -->|1| n6
  n4 -->|1| n0
  n5 -->|2| n0
  n5 -->|1| n2
  n5 -->|18| n3
  n5 -->|5| n4
  n5 -->|2| n6
  n6 -->|4| n3
  n7 -->|11| n2
  n7 -->|4| n3
Loading

Cycle edges that are one include

Each of these is a single line. Cutting it removes a directory-level dependency outright.

edge site include
emc/ini -> emc/motion src/emc/ini/inihal.hh:23 <emcmotcfg.h> = src/emc/motion/emcmotcfg.h
emc/motion -> emc/ini src/emc/motion/usrmotintf.cc:31 <inifile.hh> = src/emc/ini/inifile.hh
emc/motion -> libnml/os_intf src/emc/motion/usrmotintf.cc:28 "_timer.h" = src/libnml/os_intf/_timer.h
emc/motion -> libnml/rcs src/emc/motion/usrmotintf.cc:29 "rcs_print.hh" = src/libnml/rcs/rcs_print.hh
emc/nml_intf -> emc/tooldata src/emc/nml_intf/emcops.cc:20 "tooldata.hh" = src/emc/tooldata/tooldata.hh
emc/nml_intf -> libnml/cms src/emc/nml_intf/emc.cc:30 "cms.hh" = src/libnml/cms/cms.hh
emc/nml_intf -> rtapi src/emc/nml_intf/emcargs.cc:22 <rtapi_string.h> = src/rtapi/rtapi_string.h
emc/pythonplugin -> emc/ini src/emc/pythonplugin/python_plugin.cc:20 <inifile.hh> = src/emc/ini/inifile.hh
emc/rs274ngc -> emc/motion src/emc/rs274ngc/modal_state.hh:30 "state_tag.h" = src/emc/motion/state_tag.h
emc/rs274ngc -> hal src/emc/rs274ngc/interp_namedparams.cc:54 <hal.h> = src/hal/hal.h
emc/tp -> hal src/emc/tp/tpmod.c:11 <hal.h> = src/hal/hal.h
hal -> hal/utils src/hal/halmodule.cc:31 "setps_util.h" = src/hal/utils/setps_util.h
libnml/cms -> libnml/nml src/libnml/cms/tcp_srv.cc:40 "nml.hh" = src/libnml/nml/nml.hh
libnml/cms -> libposemath src/libnml/cms/cms_pm.cc:17 <posemath.h> = src/libposemath/posemath.h
libnml/rcs -> libnml/nml src/libnml/rcs/rcs.hh:41 "nml_type.hh" = src/libnml/nml/nml_type.hh
libnml/rcs -> libnml/os_intf src/libnml/rcs/rcs_exit.cc:23 "timer.hh" = src/libnml/os_intf/timer.hh
libnml/rcs -> rtapi src/libnml/rcs/rcs_print.cc:25 <rtapi_string.h> = src/rtapi/rtapi_string.h
rtapi -> hal src/rtapi/uspace_rtapi_main.cc:60 <hal.h> = src/hal/hal.h

The heavier knots

Cycle edges carrying two or more includes
edge sites headers
emc/kinematics -> rtapi 62 rtapi.h x20, rtapi_math.h x17, rtapi_app.h x11, rtapi_string.h x11, rtapi_ctype.h x2, rtapi_bool.h
emc/motion -> rtapi 30 rtapi.h x8, rtapi_math.h x6, rtapi_string.h x4, rtapi_bool.h x3, rtapi_app.h x2, rtapi_errno.h x2, rtapi_mutex.h x2, rtapi_atomic.h, rtapi_limits.h, rtapi_stdint.h
hal/utils -> rtapi 27 rtapi.h x13, rtapi_string.h x8, rtapi_mutex.h x5, rtapi_app.h
emc/tp -> rtapi 22 rtapi.h x8, rtapi_math.h x8, rtapi_bool.h x2, rtapi_slab.h x2, rtapi_app.h, rtapi_string.h
emc/kinematics -> hal 21 hal.h x21
emc/rs274ngc -> emc/nml_intf 18 interp_return.hh x10, canon.hh x4, emcpos.h x2, debugflags.h, emc.hh
emc/ini -> emc/nml_intf 17 emc.hh x8, emcglb.h x5, emccfg.h x3, emcpos.h
hal/utils -> hal 16 hal.h x16
hal -> rtapi 14 rtapi.h x4, rtapi_mutex.h x2, rtapi_app.h, rtapi_atomic.h, rtapi_bool.h, rtapi_byteorder.h, rtapi_errno.h, rtapi_parport.h, rtapi_stdint.h, rtapi_string.h
libnml/cms -> libnml/buffer 13 physmem.hh x4, rem_msg.hh x3, locmem.hh, phantom.hh, recvn.h, sendn.h, shmem.hh, tcpmem.hh
emc/rs274ngc -> rtapi 12 rtapi_string.h x6, rtapi_math.h x5, rtapi.h
libnml/cms -> libnml/rcs 12 rcs_print.hh x10, rcsversion.h x2
emc/tp -> libposemath 11 posemath.h x9, emcpose.h x2
emc/tp -> emc/motion 11 motion.h x4, emcmotcfg.h x2, mot_priv.h x2, axis.h, simple_tp.h, state_tag.h
libnml/nml -> libnml/cms 11 cms.hh x6, cmsdiag.hh x2, cms_cfg.hh, cms_srv.hh, cms_user.hh
emc/kinematics -> emc/motion 9 emcmotcfg.h x9
libnml/buffer -> libnml/cms 9 cms.hh x7, cmsdiag.hh, tcp_opts.hh
libnml/buffer -> libnml/os_intf 8 timer.hh x3, _timer.h x2, shm.hh x2, sem.hh
emc/motion -> hal 7 hal.h x7
emc/motion -> emc/tp 7 tp.h x3, sp_scurve.h x2, tp_debug.h, tp_types.h
libnml/buffer -> libnml/rcs 7 rcs_print.hh x7
libnml/nml -> libnml/rcs 7 rcs_print.hh x4, rcs.hh, rcs_exit.hh, rcsversion.h
libposemath -> rtapi 7 rtapi_math.h x6, rtapi_string.h
emc/ini -> libnml/rcs 5 rcs_print.hh x5
emc/nml_intf -> libnml/nml 5 cmd_msg.hh, nml.hh, nml_type.hh, nmlmsg.hh, stat_msg.hh
emc/rs274ngc -> emc/pythonplugin 5 python_plugin.hh x5
libnml/cms -> rtapi 5 rtapi_string.h x5
libnml/nml -> libnml/buffer 5 physmem.hh x3, rem_msg.hh x2
libnml/os_intf -> libnml/rcs 5 rcs_print.hh x5
emc/ini -> rtapi 4 rtapi_stdint.h x2, rtapi.h, rtapi_math.h
emc/kinematics -> libposemath 4 gomath.h x2, gotypes.h x2
emc/kinematics -> emc/nml_intf 4 emcpos.h x4
emc/nml_intf -> emc/rs274ngc 4 modal_state.hh x3, rs274ngc.hh
emc/nml_intf -> libnml/rcs 4 rcs.hh x2, rcs_print.hh x2
emc/tooldata -> emc/nml_intf 4 emc_nml.hh x2, canon.hh, emc.hh
emc/tp -> emc/nml_intf 4 emcpos.h x2, motion_types.h x2
libnml/cms -> libnml/os_intf 4 timer.hh x3, _timer.h
emc/motion -> emc/nml_intf 3 motion_types.h x2, emcpos.h
emc/motion -> libposemath 3 posemath.h x3
emc/motion -> emc/kinematics 3 kinematics.h x2, cubic.h
emc/nml_intf -> emc/motion 3 emcmotcfg.h x3
emc/nml_intf -> libposemath 3 posemath.h x2, emcpose.h
emc/ini -> hal 2 hal.h x2
emc/rs274ngc -> emc/tooldata 2 tooldata.hh x2
emc/rs274ngc -> emc/ini 2 inifile.hh x2
emc/tooldata -> rtapi 2 rtapi_mutex.h, rtapi_string.h
hal/utils -> emc/ini 2 inifile.h, inifile.hh
libnml/buffer -> rtapi 2 rtapi_string.h x2
libnml/nml -> rtapi 2 rtapi_string.h x2
libnml/nml -> libnml/os_intf 2 timer.hh x2

Exported headers with no in-tree user outside their own directory

A header reached only from its own directory is a candidate for coming off SRCHEADERS, but not automatically: posemath.hh and posemath_types.h are pulled in by posemath.h beside them, and hostmot2-serial.h is used by the modbus code, which falls in the same directory bucket. The rtapi_* entries are the ones genuinely reached from one or two places.

header users elsewhere
hal/drivers/mesa-hostmot2/hostmot2-serial.h none
libposemath/posemath.hh none
libposemath/posemath_types.h none
rtapi/rtapi_atomic.h src/emc/motion/motion.h, src/hal/hal_lib.c
rtapi/rtapi_bitops.h src/hal/drivers/hal_pi_gpio.c
rtapi/rtapi_byteorder.h src/hal/drivers/mesa-hostmot2/hm2_modbus.c, src/hal/hal.h
rtapi/rtapi_device.h src/hal/drivers/mesa-hostmot2/hostmot2-lowlevel.h
rtapi/rtapi_gfp.h src/hal/drivers/mesa-hostmot2/hm2_spi.c, src/hal/drivers/mesa-hostmot2/setsserial.c
rtapi/rtapi_limits.h src/emc/motion/motion.h
rtapi/rtapi_math_i386.h none
rtapi/rtapi_vsnprintf.h none
Full directory edge list
from to sites
emc/canterp emc/nml_intf 2
emc/canterp emc/rs274ngc 2
emc/canterp emc 1
emc/canterp src 1
emc/ini emc/nml_intf (in cycle) 17
emc/ini libnml/rcs (in cycle) 5
emc/ini rtapi (in cycle) 4
emc/ini hal (in cycle) 2
emc/ini emc/motion (in cycle) 1
emc/kinematics rtapi (in cycle) 62
emc/kinematics hal (in cycle) 21
emc/kinematics emc/motion (in cycle) 9
emc/kinematics libposemath (in cycle) 4
emc/kinematics emc/nml_intf (in cycle) 4
emc/motion rtapi (in cycle) 30
emc/motion hal (in cycle) 7
emc/motion emc/tp (in cycle) 7
emc/motion emc/nml_intf (in cycle) 3
emc/motion libposemath (in cycle) 3
emc/motion emc/kinematics (in cycle) 3
emc/motion src 1
emc/motion emc 1
emc/motion libnml/os_intf (in cycle) 1
emc/motion libnml/rcs (in cycle) 1
emc/motion emc/ini (in cycle) 1
emc/motion-logger emc/motion 4
emc/motion-logger hal 1
emc/motion-logger emc/nml_intf 1
emc/nml_intf libnml/nml (in cycle) 5
emc/nml_intf emc/rs274ngc (in cycle) 4
emc/nml_intf libnml/rcs (in cycle) 4
emc/nml_intf emc/motion (in cycle) 3
emc/nml_intf libposemath (in cycle) 3
emc/nml_intf emc 2
emc/nml_intf libnml/cms (in cycle) 1
emc/nml_intf rtapi (in cycle) 1
emc/nml_intf src 1
emc/nml_intf emc/tooldata (in cycle) 1
emc/pythonplugin emc/ini (in cycle) 1
emc/rs274ngc emc/nml_intf (in cycle) 18
emc/rs274ngc rtapi (in cycle) 12
emc/rs274ngc emc/pythonplugin (in cycle) 5
emc/rs274ngc src 2
emc/rs274ngc emc/tooldata (in cycle) 2
emc/rs274ngc emc/ini (in cycle) 2
emc/rs274ngc emc 1
emc/rs274ngc hal (in cycle) 1
emc/rs274ngc emc/motion (in cycle) 1
emc/sai emc/rs274ngc 8
emc/sai emc/nml_intf 4
emc/sai rtapi 2
emc/sai emc/tooldata 2
emc/sai emc/ini 1
emc/sai src 1
emc/sai libnml/rcs 1
emc/task emc/nml_intf 28
emc/task emc/ini 11
emc/task libnml/rcs 9
emc/task emc/motion 6
emc/task rtapi 5
emc/task libnml/os_intf 4
emc/task src 3
emc/task emc/rs274ngc 3
emc/task emc/tooldata 3
emc/task libnml/nml 3
emc/task emc/usr_intf 2
emc/task emc/pythonplugin 1
emc/task hal 1
emc/tooldata emc/nml_intf (in cycle) 4
emc/tooldata src 2
emc/tooldata rtapi (in cycle) 2
emc/tp rtapi (in cycle) 22
emc/tp libposemath (in cycle) 11
emc/tp emc/motion (in cycle) 11
emc/tp emc/nml_intf (in cycle) 4
emc/tp hal (in cycle) 1
emc/usr_intf emc/nml_intf 32
emc/usr_intf libnml/rcs 16
emc/usr_intf emc/ini 7
emc/usr_intf libnml/os_intf 7
emc/usr_intf rtapi 7
emc/usr_intf libnml/nml 6
emc/usr_intf libposemath 6
emc/usr_intf src 5
emc/usr_intf emc 3
emc/usr_intf emc/tooldata 2
emc/usr_intf emc/kinematics 1
emc/usr_intf hal 1
hal rtapi (in cycle) 14
hal hal/utils (in cycle) 1
hal/classicladder rtapi 31
hal/classicladder hal 5
hal/classicladder src 2
hal/components rtapi 68
hal/components hal 25
hal/components src 1
hal/drivers rtapi 224
hal/drivers hal 58
hal/drivers src 3
hal/user_comps rtapi 24
hal/user_comps hal 14
hal/user_comps emc/ini 4
hal/user_comps src 2
hal/utils rtapi (in cycle) 27
hal/utils hal (in cycle) 16
hal/utils src 10
hal/utils emc 2
hal/utils emc/ini (in cycle) 2
libnml/buffer libnml/cms (in cycle) 9
libnml/buffer libnml/os_intf (in cycle) 8
libnml/buffer libnml/rcs (in cycle) 7
libnml/buffer libnml/linklist 3
libnml/buffer rtapi (in cycle) 2
libnml/cms libnml/buffer (in cycle) 13
libnml/cms libnml/rcs (in cycle) 12
libnml/cms rtapi (in cycle) 5
libnml/cms libnml/linklist 5
libnml/cms libnml/os_intf (in cycle) 4
libnml/cms emc 1
libnml/cms libposemath (in cycle) 1
libnml/cms libnml/nml (in cycle) 1
libnml/nml libnml/cms (in cycle) 11
libnml/nml libnml/rcs (in cycle) 7
libnml/nml libnml/linklist 5
libnml/nml libnml/buffer (in cycle) 5
libnml/nml rtapi (in cycle) 2
libnml/nml libnml/os_intf (in cycle) 2
libnml/os_intf libnml/rcs (in cycle) 5
libnml/os_intf src 2
libnml/rcs libnml/linklist 2
libnml/rcs libnml/nml (in cycle) 1
libnml/rcs libnml/os_intf (in cycle) 1
libnml/rcs rtapi (in cycle) 1
libposemath rtapi (in cycle) 7
libposemath src 1
module_helper src 1
rtapi src 7
rtapi emc 1
rtapi hal (in cycle) 1
rtapi/examples rtapi 14
tests libposemath 1

What it says about this PR

Two of the edges this PR touches are in that cycle, and the report puts numbers on them:

  • emc/kinematics -> emc/nml_intf is 4 include sites and all four are emcpos.h. The second commit does not remove that edge by itself, since emcpos.h stays as a shim, but once those four move to libposemath/emcpose.h the edge goes entirely.
  • emc/kinematics -> libposemath is exactly the four gomath.h and gotypes.h includes the third commit changes.

The single include edges look like the cheapest place to start, whatever we do about the rest. src/rtapi/uspace_rtapi_main.cc:60 including <hal.h> is the one I would look at first, since it is what puts the bottom layer inside the cycle at all.

@BsAtHome

BsAtHome commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Very nice work. That script is a nice to have :-)

A few things:
Firstly, the dependencies may differ slightly for includes traced for normal build and the RT build. They do not use the same -I IIRC. This should be checked as well. Did you include all the components as well in the graph?

Secondly, much more difficult, is the fact that includes (can) span userspace and RT space. This is good visible in what motion and TP include and what others include from them. Several includes do not differentiate between what is needed to build (local inter-file defines, types and prototypes) and what is needed to interface to other code. That line has been a vague one, which is not exposed as well by the graph, besides it noting the inclusion. It is here that the "untangling" becomes more involved. That said, your analysis does show where to look.

Thirdly, the uspace_rtapi_main.cc use of hal.h is completely redundant. There are no references to its content whatsoever. This brings us to the point where includes are there just "because" and are not necessary at all. The include's content is not always a good well-designed separator for exposed interfaces, touching my second point. Therefore, some includes may actually be removed and would already trim down the tree a bit. Others will need some redesign.

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.

2 participants