Skip to content

Ccache build support#216

Draft
jakeheke75 wants to merge 120 commits intoTwon:mainfrom
jakeheke75:ccache_build_support
Draft

Ccache build support#216
jakeheke75 wants to merge 120 commits intoTwon:mainfrom
jakeheke75:ccache_build_support

Conversation

@jakeheke75
Copy link
Collaborator

Hi Antony,
First draft of the CCache support #113.
Thanks in advance for your time.
Cheers,
Gianmario

@codecov
Copy link

codecov bot commented Oct 9, 2023

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 96.77%. Comparing base (effbec1) to head (7a0e39e).
Report is 2 commits behind head on main.

❗ Current head 7a0e39e differs from pull request most recent head 6c25479. Consider uploading reports for the commit 6c25479 to get more accurate results

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #216      +/-   ##
==========================================
- Coverage   97.08%   96.77%   -0.31%     
==========================================
  Files         132      119      -13     
  Lines        4180     3940     -240     
==========================================
- Hits         4058     3813     -245     
- Misses        122      127       +5     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@Twon
Copy link
Owner

Twon commented Oct 11, 2023

Hey Gianmario,

Thanks for this, it looks like a great starting point. But there are lots more we should do to this including:

  • Windows support for MSVC.
  • Support for Xcode and Visual Studio (it will only support Ninja and Make by default)
  • Querying the current version of CCache (why would we do this when we use Conan to fetch a specific version, because we may also not support the buildenv where we can provide a docker image with all of the build tools in or when you want to test a more recent version locally)
  • Controlling CCache setting to tune the behaviour.
  • Support additional distributed compiler command (Distcc, Icecream or Incredibuild).
  • The ccache.cmake module should be completely self contained (so it can be copied and pasted into other project to provide drop in support). This will mean wrapping CCache functionality into a function and calling in the main CMakeLists.txt.

I'll start making some comments and providing references for why we want to do things to help explain. Any questions please let me know and hopefully we can make this a good learning experience for us both!

Cheers,
Ant

conanfile.py Outdated

def checkCCacheIsSupported(self):
""" CCache is fully supported on Linux and macOS with gcc and clang. """
return (self.settings.os == "Macos" or self.settings.os == "Linux") and \
Copy link
Owner

Choose a reason for hiding this comment

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

I think this may not be needed eventually as we should be able to get this working on Windows and with MSVC and Intel compilers too: https://github.com/ccache/ccache/wiki/MS-Visual-Studio

But let's leave it for now and focus on these to begin with.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I explain why I made this choice; because in the ccache documentation I saw that MSVC has not A support level:
image
Anyway, as you said let's leave it as it is for now. I am keeping this conversation open, in this way we are not going to forget the point.

Copy link
Owner

Choose a reason for hiding this comment

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

Ah right, I think I have seen this before. Well, let's try it out and see if it's useful on Windows. Hopefully we can tie in some capture of build metrics at some point to shape our decisions.

@jakeheke75
Copy link
Collaborator Author

I'll start making some comments and providing references for why we want to do things to help explain. Any questions please let me know and hopefully we can make this a good learning experience for us both!

Cheers, Ant
Hi Antony, thanks a lot for your comments. I will check them all and start adding features. It will be a lot of fun! 😄
Cheers,
Gianmario

@Twon
Copy link
Owner

Twon commented Oct 11, 2023

So intertingly it looks like CCache support was not actually enabled yet. I can see this by testing with the ccache show stats command as such:

05:31:08 (conan-2-py-3.10.4) antony@Antony Morpheus ±|ccache_build_support ✗|→ ccache -s
Cacheable calls:    1058 / 1063 (99.53%)
  Hits:                0 / 1058 ( 0.00%)
    Direct:            0
    Preprocessed:      0
  Misses:           1058 / 1058 (100.0%)
Uncacheable calls:     5 / 1063 ( 0.47%)
Local storage:
  Cache size (GiB):  0.4 /  5.0 ( 7.54%)
  Hits:                0 /   76 ( 0.00%)
  Misses:             76 /   76 (100.0%)

Looks like it was because there was a missing option, this was needed but not defined:

option(MORPHEUS_BUILD_WITH_CCACHE "Enable the the CCache compiler caching" ON)

Easy to miss, so I want to show you how to test the ccache stats so you can confirm things are working, and also the hit rate of the cache which we may also need to tune. I've push up a fix for this, once the fix is in place I see this which suggests it's working:

05:44:16 (conan-2-py-3.10.4) antony@Antony Morpheus ±|ccache_build_support ✗|→ ccache -s
Cacheable calls:    1134 / 1144 (99.13%)
  Hits:               76 / 1134 ( 6.70%)
    Direct:           76 /   76 (100.0%)
    Preprocessed:      0 /   76 ( 0.00%)
  Misses:           1058 / 1134 (93.30%)
Uncacheable calls:    10 / 1144 ( 0.87%)
Local storage:
  Cache size (GiB):  0.4 /  5.0 ( 7.54%)
  Hits:               76 /  152 (50.00%)
  Misses:             76 /  152 (50.00%)

@jakeheke75
Copy link
Collaborator Author

* Support additional distributed compiler command (Distcc, Icecream or Incredibuild).

Hi Antony,
About this point I would go for Icecream if you agree because it seems the most maintained if we exclude Incredibuild which is a commercial product. What do you think?
And by the way, for the package installation we can't rely on Conan right, because for those packages if I am not wrong there is not recipe. Therefore whichever choice we do we will have to install the package manually.

cmake_path(NATIVE_PATH CCACHE_BIN ccache_exe)
file(WRITE ${CMAKE_BINARY_DIR}/launch-cl.cmd
"@echo off\n"
"\"${ccache_exe}\" \"${CMAKE_C_COMPILER}\" %*\n"
Copy link
Owner

Choose a reason for hiding this comment

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

So it looks like we only generate a cmd invoke wrapper for the C compiler, is this correct? I'd presume we need this for every language supported (I may be wrong here but be good to understand the rational)?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Quoting chapter 35.4.4. Visual Studio Generators:

Using Ccache with the Visual Studio generators requires Ccache 4.6.1 or later. The
CMAKE_<LANG>_COMPILER_LAUNCHER variables don’t support Visual Studio generators, but Visual Studio
project properties can be used in conjunction with a launch script to achieve the same thing. The
CLToolPath and CLToolExe project properties redirect the compile steps to use a custom compiler
executable or script. The properties can be set with the CMAKE_VS_GLOBALS variable (see Section 35.3.3,
“Visual Studio Generators” for another closely related use of this variable).
The same compiler executable is used for C and C++ with these generators. Thus, only one launch
script is needed, unlike the Xcode generator which requires one launch script per language. 

That's the reason why I did it in this way. I could easily be wrong because I am a newbie and I am also developing on a Linux machine 😄

Copy link
Owner

Choose a reason for hiding this comment

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

Ha, it may the the correct thing to do but I think it requires a little investigation and testing for each supported language. I can tests on my Windows machine.

set(CMAKE_XCODE_ATTRIBUTE_LDPLUSPLUS ${launchCXX})
endif()
endforeach()
elseif(${CMAKE_GENERATOR} MATCHES "Visual Studio")
Copy link
Owner

Choose a reason for hiding this comment

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

I imagine with Visual Studio support we have to limit the languages to language implicitly supported by the MSbuild engine?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Ah probably yes, sorry but I am missing this one. MSBuild engine supports C and CXX I suppose. The documentation is there and from a quick sight It seems supportin C with MSBuild and CXX with MSBuild++.

Copy link
Owner

Choose a reason for hiding this comment

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

Yes, and Cuda should be good. But I'd expect Objective C/C++ to not work here. Main point here is I think we need to explicit test support for each language (and be we I mean me as I have a Windows Laptop with Visual Studio installed which I can test on)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I need to install Visual Studio also. Build instructions for Windows are explained there right?

Copy link
Owner

Choose a reason for hiding this comment

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

Hey, that's pretty close (Heisenberg used Conan 1). I should write up a page for Windows instructions too. Let me know if you hit any issues trying this out.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I am taking a bunch of notes while I am building the library for Windows. I took notes of all the steps that I did because I started from scratch. I can write a build instruction page for Windows. I will do it as soon as we have ccache working.

if(${CMAKE_GENERATOR} MATCHES "Ninja|Makefiles")
message(STATUS "found generator Ninja|Makefiles") #debug
foreach(lang IN ITEMS ${MORPHEUS_LANGUAGES})
set(CMAKE_${lang}_COMPILER_LAUNCHER ${CCACHE_BIN})
Copy link
Owner

@Twon Twon Oct 13, 2023

Choose a reason for hiding this comment

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

This is a good starting point, but this is where it gets more interesting. That is because we need to be able to pass through settings to ccache. The mechanism for this in ccache is to set environment variables for each parameter that is controllable.

CMake has a very specific way of doing this, you can use the ${CMAKE_COMMAND} variable to invoke a CMake subprocess to launch your process with environment variable propagated: https://cmake.org/cmake/help/latest/manual/cmake.1.html#run-a-command-line-tool. So for example this could be achieved using the ${CMAKE_COMMAND} -E env ${ccacheEnvVariables} ${CCACHE_BIN} command passed to the compiler launcher.

To quote the CMake Professional book's section on Ccache:

#For most projects, the above example is too simplistic. It does not set any Ccache environment variables, it
#relies purely on the user-level configuration of that tool. CMake’s command mode can be used to set the 
#relevant environment variables when invoking Ccache on any platform. The following is a more realistic example: 

find_program(CCACHE_EXECUTABLE ccache)
if(CCACHE_EXECUTABLE)
   set(ccacheEnv CCACHE_SLOPPINESS=pch_defines,time_macros) # NOTE: Ccache 4.2+ required for reliable CUDA support
   foreach(lang IN ITEMS C CXX OBJC OBJCXX CUDA)
        set(CMAKE_${lang}_COMPILER_LAUNCHER ${CMAKE_COMMAND} -E env ${ccacheEnv} ${CCACHE_EXECUTABLE})
   endforeach()
endif()

Copy link
Owner

Choose a reason for hiding this comment

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

Anyway, in summary the next step is to look at the Ccache documentation and see which variable we should be setting and then expose them by CMake variable or parameter to the enable_cache method.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Good approach. I will try to start with the following that seem useful:

CCACHE_SLOPPINESS=pch_defines,time_macros
CCACHE_PREFIX=icecc (if Icecream executable is present)
CCACHE_LOGFILE=~/.ccache/ccache_logfile.txt (to understand in the detail what ccache is doing)

I guess we will discover the others that we need once we start to build with ccache consistently. What do you think?

Copy link
Owner

Choose a reason for hiding this comment

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

Yes, completely agree! These variable sounds like a good start. I need to spend time time reading through the docs to understand all of the options but we can add incrementally to it as we build our understanding.

CMakeLists.txt Outdated
add_subdirectory(examples)

if (MORPHEUS_BUILD_WITH_CCACHE)
set(MORPHEUS_LANGUAGES CXX)
Copy link
Owner

Choose a reason for hiding this comment

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

Actually, the are also parts of Objective C/C++ in the Metal redendering library and I'm also planning for Cuda support in the future.

Perhaps we should move this out of the if statement and define this as a top-level variable for the project.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Perfect I will move it out of the if statement. The reason why I created a dedicated variable MORPHEUS_LANGUAGES is that unfortunately the project command option LANGUAGES doesn't set any variable:

project(<PROJECT-NAME>
        [VERSION <major>[.<minor>[.<patch>[.<tweak>]]]]
        [DESCRIPTION <project-description-string>]
        [HOMEPAGE_URL <url-string>]
        [LANGUAGES <language-name>...])

I will move it out of the if statement and rename it as CCACHE_LANGUAGES and pass it to the function.


find_program(CCACHE_BIN ccache REQUIRED)
if(NOT CCACHE_BIN)
message(STATUS "Morpheus: CCache not found. Rebuilding all the source files.")
Copy link
Owner

@Twon Twon Oct 14, 2023

Choose a reason for hiding this comment

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

Actually, these messages are not needed, because of the REQUIRED parameter to find_program. If ccache is not found CMake processing will stop with an error message at that point. So I don't believe these messages could ever be triggered, see docs for find_program with the REQUIRED param: https://cmake.org/cmake/help/latest/command/find_program.html

Suggested change
message(STATUS "Morpheus: CCache not found. Rebuilding all the source files.")

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

You are right, message will not be triggered because of the REQUIRED parameter. In this case I think that we should remove it, because for ccache in conanfile.py we are exposing an option build_with_ccache. Then it can effectively happen that the message will be triggered right?

@Twon
Copy link
Owner

Twon commented Oct 14, 2023

* Support additional distributed compiler command (Distcc, Icecream or Incredibuild).

Hi Antony, About this point I would go for Icecream if you agree because it seems the most maintained if we exclude Incredibuild which is a commercial product. What do you think? And by the way, for the package installation we can't rely on Conan right, because for those packages if I am not wrong there is not recipe. Therefore whichever choice we do we will have to install the package manually.

For now, I think it is enough to expose a way to set this field. Icecream and other distributed compilation tools require a co-ordinator and other nodes to offload compilation too. I have no idea how we could test this until we have a gid of machines. As long as we can set it then when the time comes we will be able to test it.

But to answer your point, yes, I'd go with Icecream as I have previously used this and its simple to set up. When were are in a position to use try this I'd expect to create a Conan package for this (which I have done for a few libraries now).

@jakeheke75
Copy link
Collaborator Author

But to answer your point, yes, I'd go with Icecream as I have previously used this and its simple to set up. When were are in a position to use try this I'd expect to create a Conan package for this (which I have done for a few libraries now).

Sorry for the newbie question, "to create a Conan package for this" it means that we will contribute to the Conan project
and add a recipe for Icecream and publish it on ConanCenter?

@Twon
Copy link
Owner

Twon commented Oct 15, 2023

But to answer your point, yes, I'd go with Icecream as I have previously used this and its simple to set up. When were are in a position to use try this I'd expect to create a Conan package for this (which I have done for a few libraries now).

Sorry for the newbie question, "to create a Conan package for this" it means that we will contribute to the Conan project and add a recipe for Icecream and publish it on ConanCenter?

Yes, exactly that. I have done this for a few libraries already so have some experience with this so if need be I think we can do this.

@jakeheke75
Copy link
Collaborator Author

Hi Antony,
So... it has been a long battle but we almost have 100% cache hit rate for all the builds and also a decent performance.
image

But the Windows build it's still tricky! If we keep the conan install flag --build catch2/3.4.0 then at each workflow run the hash of the ccache files will change and therefore we will get a low hit rate. So I tried to removed it.
If we remove it we get a linking error in the Windows build for catch2 package! Any idea about this? 🤔

LINK: command "C:\PROGRA~1\MICROS~2\2022\ENTERP~1\VC\Tools\MSVC\1438~1.331\bin\Hostx64\x64\link.exe /nologo @CMakeFiles\MorpheusCoreTests.Release.rsp /out:bin\Release\MorpheusCoreTests.exe /implib:lib\Release\MorpheusCoreTests.lib /pdb:bin\Release\MorpheusCoreTests.pdb /version:0.0 /machine:x64 /INCREMENTAL:NO /subsystem:console /MANIFEST:EMBED,ID=1" failed (exit code 1120) with the following output:
enums.tests.cpp.obj : error LNK2019: unresolved external symbol "public: static class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > __cdecl Catch::StringMaker<class std::basic_string_view<char,struct std::char_traits<char> >,void>::convert(class std::basic_string_view<char,struct std::char_traits<char> >)" (?convert@?$StringMaker@V?$basic_string_view@DU?$char_traits@D@std@@@std@@X@Catch@@SA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@V?$basic_string_view@DU?$char_traits@D@std@@@4@@Z) referenced in function "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > __cdecl Catch::Detail::stringify<class std::basic_string_view<char,struct std::char_traits<char> > >(class std::basic_string_view<char,struct std::char_traits<char> > const &)" (??$stringify@V?$basic_string_view@DU?$char_traits@D@std@@@std@@@Detail@Catch@@YA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@AEBV?$basic_string_view@DU?$char_traits@D@std@@@3@@Z)

So to get a 100% success hit rate for Windows I had to do the following trick:

  • The first time build the library for Windows with the --build catch2/3.4.0 flag. This allows to initially build correctly the Conan cache and also the ccache cache.
  • Remove the flag --build catch2/3.4.0 for Windows and then from now on we will get almost the 100% success hit rate.

The limit of this manual solution approach is that everything is working fine until we add/update packages in the conanfile.py. Then we have to play this game again in order to have ccache working properly for Windows.
The alternative is that we always build Windows with the flag --build catch2/3.4.0 and then we accept for those 2 Windows builds (Debug and Release) to always have a lower hit rate and a slightly worse performance:
image
Which is your opinion? Do we go for the manual workaround or do we build every time --build catch2/3.4.0 and at least for the moment we accept to always get a partial success rate because of the catch package rebuild?
Maybe in the future we will be able to understand why the CI Windows build fails without the --build catch2/3.4.0 flag.

Thanks,
Gianmario

@Twon
Copy link
Owner

Twon commented Dec 2, 2023

Hi Gianmario,

Great work mate, absolutely heroic effort to get this working! :)

Regarding the Catch2 issues, this is a known issue: conan-io/conan-center-index#19008. It's been causing pain for a while now, and is causing issues on other branches: https://github.com/Twon/Morpheus/actions/runs/7003719483/job/19049991146?pr=237

I should bite the bullet here and raise a PR to fix the Conan package as this has been causing issues for months now. If you don't mind holding off submitting this for a little longer I'll aim to get a fix raised in the next couple of days.

BTW, regarding your question on the next topics to address, I hope to come back shortly with a few suggestions here.

Cheers,
Antony

@jakeheke75
Copy link
Collaborator Author

Great work mate, absolutely heroic effort to get this working! :)

No struggle, no learning 😄

Regarding the Catch2 issues, this is a known issue: conan-io/conan-center-index#19008. It's been causing pain for a while now, and is causing issues on other branches: https://github.com/Twon/Morpheus/actions/runs/7003719483/job/19049991146?pr=237

I should bite the bullet here and raise a PR to fix the Conan package as this has been causing issues for months now. If you don't mind holding off submitting this for a little longer I'll aim to get a fix raised in the next couple of days.

Ah! I was not aware that this was a known issue. Let me know if I can help with this. I am looking right now at the Conan recipe for catch2/3.x.x package. In order to fix the issue is it enough to bump up the _min_cppstd version and also the _compilers_minimum_version?

image

BTW, regarding your question on the next topics to address, I hope to come back shortly with a few suggestions here.

No worries, take your time. Meanwhile I am trying to educate myself. Thanks.

Cheers,
Gianmario

@Twon
Copy link
Owner

Twon commented May 3, 2024

Quick update to say I have not forgotten about this. I have managed to get the fix to Catch2 merge now: conan-io/conan-center-index#19008 and have a fix for the similar issue with GTest: conan-io/conan-center-index#23854. Once merged this should clear the way for this to go in.

@Twon
Copy link
Owner

Twon commented May 6, 2024

I've now got the fix for GTest merged into to Conan Index Center so nothing is blocking this now. I will start working through this PR this week to understand the changes and provide some feedback. Thanks again for your heroic efforts thus far!


Visual Studio 2022 can be downloaded from Microsoft [website](https://visualstudio.microsoft.com/vs/community/)
A minimum working installation to support the project build will have the following details:
![](./VisualStudioInstall.png)
Copy link
Owner

@Twon Twon May 6, 2024

Choose a reason for hiding this comment

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

This image should be moved to a specific folder for images rather than be left in the root (we want to keep the roots as uncluttered as possible). Let's move this to <project root>/docs/images, and keep all other images there for now.

@@ -0,0 +1,58 @@
## Visual Studio and Ninja Multi-Config Build Instructions
Copy link
Owner

@Twon Twon May 6, 2024

Choose a reason for hiding this comment

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

Let's move this file to be located at <project root>/docs. The only document in the root should be theREADME.md, which should redirect to all other documentation (ideally within the docs folder, or subdirectories of that.

enable_ccacche(
[DEBUG]
[ICECC]<icecc path>
[LOGFILE]<ccache logfile path>
Copy link
Owner

@Twon Twon May 7, 2024

Choose a reason for hiding this comment

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

This is an optional parameter so does not take a logfile path.

Suggested change
[LOGFILE]<ccache logfile path>
[LOGFILE]

Two potential courses of action here, rename it to LOGGING or ENABLE_LOGFILE so it's clearly an optional value, or change it to a single value so a path location can be configured dynamically. I tend to prefer the first for simplicity.

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.

2 participants