Linking lwIP as a Library: A Standalone TCP Client on Windows
Moving lwIP out of the example_app and into a library-style build is the step that separates a learning sandbox from a real project skeleton. The three build pitfalls are not lwIP bugs but undocumented coupling points in the win32 Filelists—anyone following the official BUILDING docs will hit them on the first `cmake` invocation.
The lwIP stack moves out of the bundled example_app and into a standalone CMake project that links only the protocol core and the win32 port library. The build pulls in exactly two Filelists—one for lwipcore, one for the pcapif driver and sys_arch layer—and leaves the rest of the contrib tree out. A 159-line main.c handles the four required init steps, then spawns a thread that opens a socket to 1.1.1.1:80 and sends an HTTP GET.
Three build-breaking pitfalls surface immediately when the example scaffolding is removed: a win32 Filelists script references a target that doesn't exist unless the full contrib tree is included, PPP source files still demand a `ppp_settings.h` header even with `PPP_SUPPORT=0`, and roughly 40 errno macros collide with UCRT64's system headers unless lwIP includes come first. Each fix is a small patch or a one-line reorder, but together they expose the hidden assumptions that the example_app build quietly papers over.
The result is a clean baseline where lwIP behaves like a normal library dependency. From here, functional trimming happens entirely through `lwipopts.h` switches—disable IPv6, disable PPP, drop unused stats—and each change can be verified by recompiling and re-running the client.
lwIP's official BUILDING documentation describes a clean library-integration path, but the win32 Filelists carry an undocumented dependency on the full contrib tree that breaks the moment you follow that path literally.
The three pitfalls all share a root cause: the example_app build is a monolith that never exercises the partial-include scenario, so upstream never sees these failures.
Functional compile-time switches in lwIP (`PPP_SUPPORT`, `LWIP_IPV6`) control logic but not file inclusion—the compilation list is fixed at the Filelists level, which means unused code still compiles unless you manually exclude directories.
The errno collision is a systemic tension between lwIP's own errno values and the C library's, and the `#undef` workaround from Part 1 was always a fragile stopgap that doesn't scale past two or three macros.