-
Notifications
You must be signed in to change notification settings - Fork 21
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Rework folder structure and cmake files #298
Rework folder structure and cmake files #298
Conversation
Wow --- thanks for taking the time to share your ideas in a form that's precise enough to execute! 🙂 Yes, as predicted, all the builds broken, but that's not a problem as I understand that this PR is intended as a communication tool. I had some feedback on some of the parts, but I've run out of time/energy for tonight. I'll plan to both give that feedback later, and use this PR to help me out in playing around to make the structure more Conan-friendly. And thanks for including detailed test plans in your PR description, which will surely be invaluable with those tasks! |
OK, thanks for your patience. 🙂 It's a really exciting time at Aurora right now, so I haven't had much time/energy on weeknights to respond. But now let me provide the promised feedback for these ideas. First, a general remark, not related to the summary. As I confessed earlier, I'm extremely inexperienced at using CMake, so my knowledge of best practices is quite weak. That said, what I did learn, I learned by studying the CMake 3.29 version (i.e., 19th Edition) of Professional CMake, by Craig Scott. So a good mental model for what to expect from Au's CMake is something like "cutting edge best practices, naively applied". 😁 I'll go into some specific examples below, wherever it's relevant to things you mentioned in the summary. One example that isn't in the summary is the Now, on to the summary!
I'd prefer to keep the layout the same. I'll go into some detail explaining why. My ideal way to construct a library is to compose it out of individual, heavily unit tested library targets. I want each target to be a cohesive "unit", including the test: it should be as easy as possible to jump back and forth among the When libraries get installed to the system, of course, those three components get scattered to the winds. By contrast, I've always found developing in the more "traditional" layout extremely confusing. After all, recall that I'm going in there with the mindset of looking for the individual pieces of a unified-whole library target... and I find myself having to hunt through different folder hierarchies to get them. Worse, because it's so hard to find "the corresponding" unit test for a target, there often isn't one! Each test file may cover many targets. This means that (a) when I sit down to write a test, I have no idea where to put it, and (b) it's far too easy for a completely untested library to hide. For (b), yes, coverage tools can help and are needed anyway, but I think it's ideally "both/and" rather than "either/or". mp-units is a good example of where I've felt this way. It's a very well written library that has excellent test coverage. But the test files are far from the headers, and most headers don't have one corresponding test flie, so I find there's a lot of friction every time I sit down to write a test. Even if I simply pick a place to put the test, I can't be confident that the next person (including "me 6 months later") would pick the same test file to put a similar test. This makes me worry that logically similar tests would get diffused throughout the test folder over time. A couple of side remarks:
This is pure speculation, but I suspect that we can trace the evolution of the traditional layout to a time when it wasn't as easy for something like CMake to distribute headers, object files, and tests automatically --- a time when automated tests were thought of as "nice-to-have", or in many cases not thought of at all. By contrast, monorepo projects (such as the one that birthed Au) never had a separate "install" step, and generally grew up in an era where automated testing and high coverage were simply assumed. In this context, it seems very natural to me to group all the files for a target in one place (including the test file). I hope this lengthy section doesn't come across as a rant. 🙂 I simply felt that, if I'm departing from established common practice, it behooves me to explain why.
Yes, It does feel like 2 new options is the right amount. One should be to disable building the tests. The other should be to opt out of taking a googletest dependency at all, thereby losing access to the That said, I don't think we actually need
I believe we already do this; see: au/cmake/HeaderOnlyLibrary.cmake Line 65 in 030298c
Awesome! Having these concrete test cases, which we can adapt to whatever solution we end up with, is a huge boon --- much appreciated.
Thank you for putting in all this effort to explain what you were saying! Let's take stock of where we're at, and where we hope to end up. In order to support conan, here are the steps I see:
LMK what you think of this plan, and thanks again for helping make Au more broadly accessible! |
I wanted to update my thoughts here, because I ran across this YouTube video: CppCon 2019: Robert Schumacher “Don't Package Your Libraries, Write Packagable Libraries! (Part 2)” Above, I argued that modern CMake obviates the need to separate headers, source files, and tests in a project's structure, for purposes of making it easy to install and/or deploy. After all, defining CMake targets and their options is how we define where the files should go. I think that's probably still true, if we're using modern CMake. But what the above talk made me appreciate is that CMake isn't all that's out there --- there may be other, private build systems that I've never even heard of. If we structure our library according to how it's likely to be installed on any system, then we maximize the number of build systems that people can actually use with our library. I still think it's better on balance to keep tests integrated with their code, but I wanted to record for posterity this compelling counter-argument that I had encountered for the first time. |
This is a rough PR to illustrate what I meant here #215 (comment)
The diff is huge because I moved pretty much every source file to follow a more conventional layout.
include/*
tests/*
Two options were added to the top level CMakeLists:
AU_BUILD_TESTS
: Allows the consumer to disable building tests which greatly improves build times when au is a subproject / being packaged. This also allows the consumer to completely ditch the dependency on GTest if wanted (E.g. for packaging).AU_USE_VENDORED_GTEST
: Allows the consumer to use a version of GTest already present on the system. This not only makes this lib more flexible, but it greatly speeds up rebuilding from scratch.An alias library
Au::au
was added. This is a CMake best practice and provides uniform usability of this library in all scenarios. Without it a project that includesau
as a submodule would have to link to theau
target whereas the projects that include FetchContent or a local install would link to theAu::au
target. Now on every scenario, the consumer links toAu::au
.I've tested both scenarios:
Local install
A developer build au from sources and installs it
In this case I've used the system gtest, all tests passed
Then another project can consume it by just pointing to the install dir (if a --prefix was used, it is not needed for system libs)
built with
Using a submodule
Alternatively some project could include au as a submodule. The CMake for this project just replaces the find_package call with
add_subdirectory()
. In this case I've completely disabled the unit tests to illustrate a real world scenariobuild with
Disclaimer
Once again -- this is a rough, illustrative PR. I'm sure it broke the whole bazel build since I didn't even try to make it build there. I'm not familiar with it.