Fix C examples for Windows, build these in CI
It started with addition of a CMake config to build the examples in CI. (requested by @junhochoi)
Then it was extended by a libev build recipe which was missing on Windows.
Following that, the C examples needed fixes in order to build on Windows.
Finally, figuring out that libev needed _open_osfhandle allowed the examples to pass runtime tests.
See individual commits for details.
uthash is not available in homebrew. So user need to download the file manually in MacOS. Since it is only a single file, I suggest we put an uthash.h in the examples folder. Or we define a find_uthash cmake module.
uthash is not available in homebrew. So user need to download the file manually in MacOS. Since it is only a single file, I suggest we put an uthash.h in the examples folder. Or we define a find_uthash cmake module.
On Linux it is pretty widely available: https://pkgs.org/download/uthash, https://pkgs.org/download/uthash-dev, https://pkgs.org/download/uthash-devel
For macOS/Windows, the Travis CI config previously cloned the repo. I've now changed the CMake config to download the file if missing. Would this address your feedback?
I appreciate the work and effort here, thanks. However, I'm concerned that this adds more complexity around examples whose main intention is to demonstrate the quiche API. Unfortunately, by being methodical, the PR adds a lot of code; for example client.c has ~300 LoC and this PR nets out at about 50 extra lines of windows-specific code.
I'm split on whether this is worth the maintenance burden.
I'm split on whether this is worth the maintenance burden.
I have a same concern that this will add some more code complexity but there was already a number of questions on Window build (rust and C both -- https://github.com/cloudflare/quiche/issues?q=is%3Aissue+windows+) answered individually, so I think it's good to provide some working examples here.
These questions do not relate to using quiche but rather how to write a UDP network application on $PLATFORM. A simpler option is to file a PR for our readme or docs that simply spells out that windows C examples are not officially supported but provide some pointers elsewhere.
The Rust examples work pretty well across platforms and didn't need much invasive change to the code. The C examples exercise the FFI but it's a pretty thin layer for any cross platform issues to crop up.
However, I'm concerned that this adds more complexity around examples whose main intention is to demonstrate the quiche API. Unfortunately, by being methodical, the PR adds a lot of code; for example client.c has ~300 LoC and this PR nets out at about 50 extra lines of windows-specific code.
Windows support unfortunately incurs extra code due to API differences, but some of the additional lines are also due to issues in the existing code (incomplete error handling). My primary motivation for adding this was to ensure it builds and works in CI. (Which was prompted by another comment :))
To make the examples easier to read for implementors focused on quiche, what do you think about sharing a common file between implementations? Candidates are:
- Random number generation for SCID. Details are not important for the example.
- Socket initialization. Not very exciting, but platform-dependent.
Such a move would have as disadvantage of requiring two or three files (example and utility source + utility header (if the utility is not directly #included)). On the other hand, it would improve clarity by hiding uninteresting details and potential for slightly better error handling.
If the hints are moved to another document without CI coverage, I am afraid they will bitrot.
However, I'm concerned that this adds more complexity around examples whose main intention is to demonstrate the quiche API. Unfortunately, by being methodical, the PR adds a lot of code; for example client.c has ~300 LoC and this PR nets out at about 50 extra lines of windows-specific code.
Windows support unfortunately incurs extra code due to API differences, but some of the additional lines are also due to issues in the existing code (incomplete error handling). My primary motivation for adding this was to ensure it builds and works in CI. (Which was prompted by another comment :))
To make the examples easier to read for implementors focused on quiche, what do you think about sharing a common file between implementations? Candidates are:
- Random number generation for SCID. Details are not important for the example.
- Socket initialization. Not very exciting, but platform-dependent.
Such a move would have as disadvantage of requiring two or three files (example and utility source + utility header (if the utility is not directly
#included)). On the other hand, it would improve clarity by hiding uninteresting details and potential for slightly better error handling.If the hints are moved to another document without CI coverage, I am afraid they will bitrot.
Sharing common file is a good idea. As a user, I am not concerned about the SCID details anyway.
My personal preference is to keep single files, and minimize both the magic and the complexity to bare minimum.
Also a personal opinion. As someone with next to no CMake experience, owning so much CMake code raises some concerns about the ability to maintain it and the time it might consume. But this is an uneducated concern.
My personal preference is to keep single files, and minimize both the magic and the complexity to bare minimum.
Also a personal opinion. As someone with next to no CMake experience, owning so much CMake code raises some concerns about the ability to maintain it and the time it might consume. But this is an uneducated concern.
Splitting to different files seems to increase the complexity of the building process by a little bit but improves the code readability by a lot. I think CMake build file is rather easier to maintain and understand if you have to do the cross-platform building. The CMake module of finding libev and uthash can be ignored if the user is not concerned about the detail.
My pessimistic view is that this will generate more questions, not stem the tide, and we will end up having to debug strange cross platform build issues in CI.
If we are able to "hide" platform details then what is the point in adding more platform builds?
Following the feedback I have made the following changes:
- Split the non-Windows changes into a new commit.
- Remove perror rename, solve the errno issue with a macro on Windows. This reduces the number of code changes. The
#define perror print_socket_errormacro is not ideal since it is magic, but I think this is a reasonable trade-off to keep the other parts unchanged. - Add comments to make
#if/else/endifboundaries clearer (/* ! _WIN32 */), moveev_io_initcall to avoid an unneeded extra variable on Linux (again to reduce the number changes for Linux). - Addressed suggestions from @junhochoi (.travis.yml comment rename and CMake version check on Windows.)
- Added some comments to the CMakeLists.txt file. This should hopefully help others understand what is going on without being familiar with CMake.
- Updated commit messages to clarify motivation for the current approach.
No extra file was introduced to keep the examples self-contained. Users not interested in Windows should be able to skip through all #ifdef _WIN32 lines and either delete or mentally ignore those blocks.
Normally platform differences can be ignored by using an abstraction library such as GLib for the event loop and GSocket for network I/O, but that might be unfamiliar to the reader and requires another dependency.
Additional platform builds can help diagnosing potential problems such as conflicting symbols in the header (this would require -Werror on GCC/Clang or /WX on MSVC to be effective, perhaps I should add /WX in the next revision?). One thing that I also forgot to add is a regression check for #507 where inclusion of a bare header failed due to missing headers.
@LPardue As for CMake, you already have experience with it since you are building boringssl :-) The big advantage for it is the ability to generate cross-platform build scripts, and the ability to use ninja for blazing fast parallel builds. For C and C++ projects, CMake is much more pleasant to maintain and use than autotools (configure.ac, Makefile.am, .m4, etc) and has accessible documentation. I am diverging from the topic here, but happy to chat this over a beer if you like. If you do not want to talk about build systems in that case, that is also understandable :)