Skip to content
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

Support system libs #92

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

bretbrownjr
Copy link
Contributor

@bretbrownjr bretbrownjr commented Dec 28, 2024

Problem

Given:

A sandbox environment is an environment where dependencies
are all provided but access to the internet is not. Also take as
a given that the build for this project happens in an environment
that meets that description.

When:

Configuring this project with any of the provided approaches. For
instance:

cmake -B build -S .

Then:

The configuration step hangs or fails, depending on the nature of
the sandboxed environment.

Solution

Alter FetchContent_* calls in this project to use GTest as the
project name and nothing else. This is important to allow the
FETCHCONENT_TRY_FIND_PACKAGE_MODE feature to work as designed.
This allows the human or tool building this project to explicitly
disable operations involving calls to git or other off-box I/O.

The documentation in the README was also updated to include
workflow tweaks for building against packaged versions of either
existing GoogleTest source code or prebuilt GoogleTest libraries.

@bretbrownjr
Copy link
Contributor Author

bretbrownjr commented Dec 28, 2024

This is ready to review, though it includes the change in #91, so I'll leave it in draft for now. We could either merge this PR with both changes and close #91 or rebase this change on top of #91 after it is merged.

EDIT: #91 is merged, so no this PR is ready to evaluate.

CMakeLists.txt Outdated
FetchContent_MakeAvailable(googletest)
endblock()
else()
find_package(GTest REQUIRED)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there never a case where we don't need google test? I'm actually hoping projects use something else mostly

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Projects that test another way wouldn't need googletest, I suppose. In particular, projects providing entirely constexpr features wouldn't benefit from a framework like googletest, so the extra dependency would get removed. Also, some libraries would be too low level to justify a dependency on a test framework that pulls in vectors and serializers and such.

I would still expect a CMake test target to exercise a "test suite" of some sort, even if that's a series of example main functions that get compiled and executed.

Copy link
Member

@JeffGarland JeffGarland Jan 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

entirely constexpr features wouldn't benefit from a framework

Well, in my experience with this in boost date-time I still had runtime tests along with the constexpr validation tests -- of course in that case the runtime tests came first and constexpr later. Still, with a trust-but-verify thinking I'd still want both compile and runtime covered -- but I wouldn't need/use the massive google test for simple test cases.

At the moment I'm not sure how to resolve the issue -- perhaps we leave a comment that says 'replace test framework here' -- not sure. In the end I wouldn't want to leave the impression that gtest is the sanctioned test framework.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you have valid concerns, Jeff. Let's keep iterating until we have something we're happy with.

That being said, the current PR is entirely about not getting the build to hang given the dependency. Are OK branching off another discussion so we can resolve the bug noted in the meantime?

@JeffGarland
Copy link
Member

except for my question, I have no issues with the PR

CMakeLists.txt Outdated Show resolved Hide resolved
@camio
Copy link
Member

camio commented Jan 17, 2025

FetchContent already has this functionality built-in. I don't think we need this PR. See FETCHCONTENT_TRY_FIND_PACKAGE_MODE.

@JeffGarland
Copy link
Member

Interesting -- would it be more correct to say we need a different PR? I think there's still a bit of an issue in the original cmake code.

@camio
Copy link
Member

camio commented Jan 20, 2025

I think there's still a bit of an issue in the original cmake code.

Can you elaborate on that?

@JeffGarland
Copy link
Member

yeah, sorry -- what I mean is the file as is unconditionally requires google test -- so one might get the impression that it's a requirement for any testing when it's not. I suppose it's fine to leave for now. @camio are you suggesting we abandon this PR?

@camio
Copy link
Member

camio commented Jan 22, 2025

what I mean is the file as is unconditionally requires google test

The file as-is only pulls in google test when BEMAN_EXEMPLAR_BUILD_TESTS is set to ON and this cmake option defaults to ON only when the project is top-level.

@camio are you suggesting we abandon this PR?

Yes

Problem
-------

**Given**:

A sandbox environment is an environment where dependencies
are all provided but access to the internet is not. Also take as
a given that the build for this project happens in an environment
that meets that description.

**When**:

Configuring this project with any of the provided approaches. For
instance:

```
cmake -B build -S .
```

**Then**:

The configuration step hangs or fails, depending on the nature of
the sandboxed environment.

Solution
--------

Alter `FetchContent_*` calls in this project to use `GTest` as the
project name and nothing else. This is important to allow the
`FETCHCONENT_TRY_FIND_PACKAGE_MODE` feature to work as designed.
This allows the human or tool building this project to explicitly
disable operations involving calls to `git` or other off-box I/O.

The documentation in the README was also updated to include
workflow tweaks for building against packaged versions of either
existing GoogleTest source code or prebuilt GoogleTest libraries.
@bretbrownjr
Copy link
Contributor Author

I don't believe the failing msvc-debug version of this build should have been affected by this change. @wusatosi or @neatudarius can you confirm?

@bretbrownjr bretbrownjr marked this pull request as ready for review January 24, 2025 15:06
@wusatosi
Copy link
Member

wusatosi commented Jan 24, 2025

I don't believe the failing msvc-debug version of this build should have been affected by this change. @ wusatosi or @ neatudarius can you confirm?

Looks like the MSVC family of compiler is having trouble finding google test dependency.

D:\a\exemplar\exemplar\build\msvc-debug_deps\gtest-src\googletest\src\gtest-all.cc(38): fatal error C1083: Cannot open include file: 'gtest/gtest.h': No such file or directory

I will try to investigate.

btw bret can you check your email/ linkedin? @bretbrownjr

Edit: confirmed MSVC family of CI fails is caused by this change. I manually triggered the workflow and it ran without fault. See: manually triggered workflow

Is *-debug_deps\gtest-src\googletest\src\gtest-all.cc well formed on other presets? Maybe other compilers are just smarter and searched more path.

@@ -325,6 +325,30 @@ cmake -B build -S . -DBEMAN_EXEMPLAR_BUILD_TESTING=OFF

</details>

<details>
<summary> Disable `git clone` operations </summary>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: maybe Disable pulling Google Test from GitHub would be a more clear summary here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not specifically GitHub that is the issue -- it's the off-box I/O as such.

Is the confusion that it's not obvious that git clone is being invoked by FetchContent? Possibly we need to update some comments in the CMakeLists.txt to make that more clear.

For instance, in Ubuntu, source code for GoogleTest is provided via a
debian-style package under the directory `/usr/src/googletest`. To
use those sources instead of cloning other sources from the internet,
provide `-DFETCHCONTENT_SOURCE_DIR_GTEST=/usr/src/googletest` as a
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't forget to add a check in CI that this works. There's no way to assert there's no actual fetching, but it would be nice to make sure this works with a well-formed setup.

See: https://github.com/bemanproject/exemplar/blob/main/.github/workflows/ci_tests.yml#L142

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could technically config a void proxy for git and then test it.

See: https://gist.github.com/evantoli/f8c23a37eb3558ab8765

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would involve interesting CI changes in each case, possibly on another PR?

  • The Ubuntu images would need the GoogleTest package installed for these flags to work as designed
  • We haven't selected a package manager on Windows -- though we should add support for building against Conan and vcpkg dependencies (GoogleTest in this case) as you and I discussed in person a little bit yesterday

Given we're dealing with an existing bug, I think it's OK to resolve the bug (once we can figure out why Windows is being weird) and circle back on a regression prevention test, though maybe @bemanproject/leads want to chime in.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's fair, I thought it would be as easy as git clone https:github.com/google/googletest /usr/libs/googletest

I usually do prefer having all CMake parameters tested (I don't think multi platform is that important here, the goal is to show the project could build under the parameters). When they break, they break silently, which is very annoying.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's fair, I thought it would be as easy as git clone https:github.com/google/googletest /usr/libs/googletest

Fair. That's technically an exercise of the workflow for one of the two documented use cases. Though it's the functional equivalent of re-implementing a worse version of FetchContent, so I'd still rather hold off until we have packaged GoogleTest source to build against as such. If for no other reason, to leave time to work on more meaningful problems elsewhere in this repo.

@steve-downey
Copy link
Member

steve-downey commented Jan 24, 2025 via email

@bretbrownjr
Copy link
Contributor Author

Just to save people time combing through the logs. Here is a more technical and detailed bug description for the CI failing here.

The failing compile command is:

C:\PROGRA~1\MICROS~2\2022\ENTERP~1\VC\Tools\MSVC\1442~1.344\bin\Hostx64\x64\cl.exe  /nologo /TP  -ID:\a\exemplar\exemplar\build\msvc-debug\_deps\gtest-src\googlemock\include -ID:\a\exemplar\exemplar\build\msvc-debug\_deps\gtest-src\googlemock -ID:\a\exemplar\exemplar\build\msvc-debug\_deps\gtest-src\include -ID:\a\exemplar\exemplar\build\msvc-debug\_deps\gtest-src /DWIN32 /D_WINDOWS   /permissive- /fsanitize=address /Zi /Ob0 /Od /RTC1 -MDd -Zi -GS -W4 -WX -wd4251 -wd4275 -nologo -J -D_UNICODE -DUNICODE -DWIN32 -D_WIN32 -DSTRICT -DWIN32_LEAN_AND_MEAN -wd4702 -utf-8 -DGTEST_HAS_PTHREAD=0 -EHsc -D_HAS_EXCEPTIONS=1  -std:c++20 /showIncludes /Fo_deps\gtest-build\googlemock\CMakeFiles\gmock_main.dir\src\gmock-all.cc.obj /Fdlib\gmock_mainpdb_debug_postfix-NOTFOUND.pdb /FS -c D:\a\exemplar\exemplar\build\msvc-debug\_deps\gtest-src\googlemock\src\gmock-all.cc

The error in the log is:

D:\a\exemplar\exemplar\build\msvc-debug\_deps\gtest-src\googlemock\include\gmock/internal/gmock-port.h(57): fatal error C1083: Cannot open include file: 'gtest/internal/gtest-port.h': No such file or directory

Notably the source file being built is at:

D:\a\exemplar\exemplar\build\msvc-debug\_deps\gtest-src\googlemock\src\gmock-all.cc

Which would lead me to believe we would expect to see an include directory like this in the compile command:

-ID:\a\exemplar\exemplar\build\msvc-debug\_deps\gtest-src\googletest\include

See the relevant path in the googletest repo for reference.

But we don't see that include path. We see these instead, presumably intended to point at the googletest headers:

-ID:\a\exemplar\exemplar\build\msvc-debug\_deps\gtest-src\include
-ID:\a\exemplar\exemplar\build\msvc-debug\_deps\gtest-src

@bretbrownjr
Copy link
Contributor Author

bretbrownjr commented Jan 25, 2025

So. @camio. I think using this approach to support FetchContent and find_package is actually broken for arcane CMake reasons involving name collisions. I'll try to explain.

First, the latest commit changes the FetchContent "name" from googletest to GTest. This is required because that name must match the find_package "PackageName" for FetchContent to be able to call a find_package that actually resolve to the FindGTest.cmake file it expects to find.

Second, googletest use a variable named gtest_SOURCE_DIR to set its include paths using include_directories(...) link (aside: Boo! Bad CMake practice compared to target_include_directories).

Third, FetchConent will define <name>_SOURCE_DIR as part of its behavior [doc link].

Fourth, the project(gtest ...) call in googletest [source link] will define gtest_SOURCE_DIR since gtest is the project name [doc link].

Fifth, it's worth noting that googletest is actually two standalone CMake projects colocated in one git repo that is a superproject stitching them together. So FetchContent sets gtest_SOURCE_DIR to point to the directory above where the googletest/ source code actually resides.

Finally, CMake is case insensitive largely, so don't get hung up on the distinction between "GTest" and "gtest" in correlating the above.

Anyway, all this leads me to believe that, in the currently proposed patch:

  • FetchContent is declaring gtest_SOURCE_DIR to point to the top-level directory of the github:googletest/googletest

  • Eventually the googletest/ subdir (a.k.a. gtest CMake "project") runs, using that value of gtest_SOURCE_DIR instead of the actual project directory for that project.

  • The include directories are therefore missing the googletest/ subdir and we get missing includes.

This concludes my submission for the 2025 international CMake debugging championships.

EDIT: I forgot to mention that I suspect that case insensitive filesystems could explain why this only happens on Windows, though I haven't identified the precise path or filesystem at play here.

@bretbrownjr
Copy link
Contributor Author

As to solutions... my personal opinion is and has been that putting FetchContent calls in regular CMakeLists.txt is a code smell and this debugging session hasn't softened that opinion.

To that end, we could move logic that provides GTest::Main, etc., to an include(resolve_dependencies.cmake) or somesuch that allows us to more gracefully and semantically select between vendoring github source code into the build versus discovering prebuilt GoogleTest on the system. Either immediately or eventually we could put hardcoded dependency info (URLs, names, etc.) in a JSON file and not in CMake.

The option that is the least amount of work is to go back to the previous implementation on this PR -- reintroduce a user-facing option to use FetchContent or not for GoogleTest.

@bretbrownjr
Copy link
Contributor Author

Oh, and as to why only failing in Windows... case insensitive filesystems?

@bretbrownjr bretbrownjr marked this pull request as draft January 26, 2025 19:58
@bretbrownjr
Copy link
Contributor Author

Since there are design and/or CMake practices questions now, this PR is not ready to merge. I'm moving it back into draft status.

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.

5 participants