Public API and headers again

I see this general issue has been discussed in the past, but I just stumbled across a new question: is there an existing internal include file where stuff from types.h should be moved if it’s not appropriate for public API? Alternatively, can internal headers (like util/alloc.h) be exported since they’re basically already being used in types.h?

Details:

I tried changing the elist_append macro defined in lib/common/types.h to an inline function to make type errors more obvious (I already noted a case of an nlist which is freed using free_list, which seems more intended for elist, but the reason is not important here).

Unfortunately, it seems that some of the elist macros use gv_recalloc and gv_calloc, which are defined in lib/util/alloc.h. Fine include that. But types.h is public API, and lib/util/alloc.h is not, so API test cases all fail to compile. They only worked before because none of the test cases used the macros elist_append(,) and alloc_elist(,) defined in types.h, so the undefined symbols gv_recalloc and gv_calloc never showed up.

Is it too late to move these uses of gv_recalloc and gv_calloc to another (internal) header file, or are they already part of the de-facto “public API”? It’s not impossible that some user out there is using elist_append and has defined their own gv_recalloca and gv_calloc to work with it. Can we export lib/util/alloc.h to public API? What about other internal machinery such as lib/util/list.h, which might be useful to make elist a bit more efficient?

-Brian

I’ve had these thoughts myself in the past. Quoting 87553d26d78d0081a0a2e116ff5f46501a001e49:

It looks a bit strange putting a reference to an internal function, gv_recalloc, that is not even in scope into a public header. But this is essentially equivalent to the previous code. ALLOC is also internal and is not in scope here. Among other concerns, this is pretty strong evidence that no users are relying on this macro.

My 2c is that it’s not worth an API break to move this, but at the next otherwise motivated API break we could do this at the same time.

Is there a process to decide when to break API? How has it been done in the past?

No formal process. Usually someone posts an API-breaking change, then we debate the merits of the change vs the impact to downstream ecosystems. Essentially a break has to bring enough benefit to users (not Graphviz developers) for it to be worth it.

This is sort of anecdotal, but IME the downstream most impacted by these changes is macOS. Windows users are typically not using Graphviz as a library, so don’t care about API. Linux and BSD users get Graphviz via their native packaging repositories that have mature mechanisms for signalling and managing API breaks.

To what extent do you think there could be an audience of Windows users that are using Graphviz through python? I have only superficial experience of their world of pip and “wheels” and installing precompiled libraries but I think that’s how they do that - they would be relying on a 3rd party to sort out the details.

Good point. There is some evidence there are downstream Windows-supporting wrappers that have no established mechanism for handling a Graphviz API break: