Repository: nodejs/node
Pull request: Enable ccache on Windows v2

This is a continuation of work that started [here](https://github.com/nodejs/node/pull/56705) and considers review comments. Since there were 2 potential approaches, this PR implements the one not represented in the previous PR. For more details, you can just read that one's description, but in short, the idea here is to disable PCH when using ccache and Clang (MSVC cannot compile with PCH). This makes initial compilation twice as long as when using PCH, but the following ones are much faster.

The fIrst 3 commits in this PR are the same as the ones in the previous ones, the only difference is [the 4th one](https://github.com/nodejs/node/pull/56847/commits/185bf60ee967cdb633520e4717987cfa2c886bb5).

Refs: https://github.com/nodejs/node/pull/56705

Changed files:
filename: BUILDING.md
patch: @@ -573,6 +573,24 @@ export CC="ccache cc"          # add to ~/.zshrc or other shell config file
 export CXX="ccache c++"        # add to ~/.zshrc or other shell config file
 ```
 
+On Windows:
+
+Tips: follow <https://github.com/ccache/ccache/wiki/MS-Visual-Studio>, and you
+should notice that obj file will be bigger the normal one.
+
+First, install ccache, assume ccache install to c:\ccache, copy
+c:\ccache\ccache.exe to c:\ccache\cl.exe with this command
+
+```powershell
+cp c:\ccache\ccache.exe c:\ccache\cl.exe
+```
+
+When building Node.js provide a path to your ccache via the option
+
+```powershell
+.\vcbuild.bat ccache c:\ccache\
+```
+
 This will allow for near-instantaneous rebuilds when switching branches back
 and forth that were built with cache.
 

filename: configure.py
patch: @@ -1018,6 +1018,11 @@
     default=None,
     help='Configure for clang-cl on Windows. This flag sets the GYP "clang" ' +
          'variable to 1 and "llvm_version" to the specified value.')
+parser.add_argument('--use-ccache-win',
+    action='store_true',
+    dest='use_ccache_win',
+    default=None,
+    help='Use ccache for compiling on Windows. ')
 
 (options, args) = parser.parse_known_args()
 
@@ -1194,6 +1199,8 @@ def get_gas_version(cc):
 # check involves checking the build number against an allowlist.  I'm not
 # quite prepared to go that far yet.
 def check_compiler(o):
+  o['variables']['use_ccache_win'] = 0
+
   if sys.platform == 'win32':
     if options.clang_cl:
       o['variables']['clang'] = 1
@@ -1202,6 +1209,9 @@ def check_compiler(o):
       o['variables']['clang'] = 0
       o['variables']['llvm_version'] = '0.0'
 
+    if options.use_ccache_win:
+      o['variables']['use_ccache_win'] = 1
+
     if not options.openssl_no_asm and options.dest_cpu in ('x86', 'x64'):
       nasm_version = get_nasm_version('nasm')
       o['variables']['nasm_version'] = nasm_version

filename: tools/msvs/props_4_ccache.props
patch: @@ -0,0 +1,13 @@
+<Project>
+  <PropertyGroup>
+    <UseMultiToolTask>true</UseMultiToolTask>
+    <TrackFileAccess>false</TrackFileAccess>
+  </PropertyGroup>
+  <ItemDefinitionGroup>
+    <ClCompile>
+      <!-- /Z7 of cl.exe, ref: https://learn.microsoft.com/en-us/cpp/build/reference/z7-zi-zi-debug-information-format -->
+      <DebugInformationFormat>OldStyle</DebugInformationFormat>
+      <ObjectFileName>$(IntDir)%(FileName).obj</ObjectFileName>
+    </ClCompile>
+  </ItemDefinitionGroup>
+</Project>

filename: tools/v8_gypfiles/v8.gyp
patch: @@ -61,7 +61,7 @@
       'type': 'none',
       'toolsets': ['host', 'target'],
       'conditions': [
-        ['OS=="win"', {
+        ['OS == "win" and (clang != 1 or use_ccache_win != 1)', {
           'direct_dependent_settings': {
             'msvs_precompiled_header': '<(V8_ROOT)/../../tools/msvs/pch/v8_pch.h',
             'msvs_precompiled_source': '<(V8_ROOT)/../../tools/msvs/pch/v8_pch.cc',

filename: vcbuild.bat
patch: @@ -28,6 +28,7 @@ set target_env=
 set noprojgen=
 set projgen=
 set clang_cl=
+set ccache_path=
 set nobuild=
 set sign=
 set nosnapshot=
@@ -87,6 +88,7 @@ if /i "%1"=="vs2022"        set target_env=vs2022&goto arg-ok
 if /i "%1"=="noprojgen"     set noprojgen=1&goto arg-ok
 if /i "%1"=="projgen"       set projgen=1&goto arg-ok
 if /i "%1"=="clang-cl"      set clang_cl=1&goto arg-ok
+if /i "%1"=="ccache"        set "ccache_path=%2%"&goto arg-ok-2
 if /i "%1"=="nobuild"       set nobuild=1&goto arg-ok
 if /i "%1"=="nosign"        set "sign="&echo Note: vcbuild no longer signs by default. "nosign" is redundant.&goto arg-ok
 if /i "%1"=="sign"          set sign=1&goto arg-ok
@@ -206,6 +208,7 @@ if defined debug_nghttp2    set configure_flags=%configure_flags% --debug-nghttp
 if defined openssl_no_asm   set configure_flags=%configure_flags% --openssl-no-asm
 if defined no_shared_roheap set configure_flags=%configure_flags% --disable-shared-readonly-heap
 if defined DEBUG_HELPER     set configure_flags=%configure_flags% --verbose
+if defined ccache_path      set configure_flags=%configure_flags% --use-ccache-win
 if defined compile_commands set configure_flags=%configure_flags% -C
 
 if "%target_arch%"=="x86" (
@@ -364,6 +367,7 @@ if "%target%"=="Build" (
 if "%target%"=="node" if exist "%config%\cctest.exe" del "%config%\cctest.exe"
 if "%target%"=="node" if exist "%config%\embedtest.exe" del "%config%\embedtest.exe"
 if defined msbuild_args set "extra_msbuild_args=%extra_msbuild_args% %msbuild_args%"
+if defined ccache_path set "extra_msbuild_args=%extra_msbuild_args% /p:TrackFileAccess=false /p:CLToolPath=%ccache_path% /p:ForceImportAfterCppProps=%CD%\tools\msvs\props_4_ccache.props"
 @rem Setup env variables to use multiprocessor build
 set UseMultiToolTask=True
 set EnforceProcessCountAcrossBuilds=True
@@ -800,7 +804,7 @@ set exit_code=1
 goto exit
 
 :help
-echo vcbuild.bat [debug/release] [msi] [doc] [test/test-all/test-addons/test-doc/test-js-native-api/test-node-api/test-internet/test-tick-processor/test-known-issues/test-node-inspect/test-check-deopts/test-npm/test-v8/test-v8-intl/test-v8-benchmarks/test-v8-all] [ignore-flaky] [static/dll] [noprojgen] [projgen] [clang-cl] [small-icu/full-icu/without-intl] [nobuild] [nosnapshot] [nonpm] [nocorepack] [ltcg] [licensetf] [sign] [x64/arm64] [vs2022] [download-all] [enable-vtune] [lint/lint-ci/lint-js/lint-md] [lint-md-build] [format-md] [package] [build-release] [upload] [no-NODE-OPTIONS] [link-module path-to-module] [debug-http2] [debug-nghttp2] [clean] [cctest] [no-cctest] [openssl-no-asm]
+echo vcbuild.bat [debug/release] [msi] [doc] [test/test-all/test-addons/test-doc/test-js-native-api/test-node-api/test-internet/test-tick-processor/test-known-issues/test-node-inspect/test-check-deopts/test-npm/test-v8/test-v8-intl/test-v8-benchmarks/test-v8-all] [ignore-flaky] [static/dll] [noprojgen] [projgen] [clang-cl] [ccache path-to-ccache] [small-icu/full-icu/without-intl] [nobuild] [nosnapshot] [nonpm] [nocorepack] [ltcg] [licensetf] [sign] [x64/arm64] [vs2022] [download-all] [enable-vtune] [lint/lint-ci/lint-js/lint-md] [lint-md-build] [format-md] [package] [build-release] [upload] [no-NODE-OPTIONS] [link-module path-to-module] [debug-http2] [debug-nghttp2] [clean] [cctest] [no-cctest] [openssl-no-asm]
 echo Examples:
 echo   vcbuild.bat                          : builds release build
 echo   vcbuild.bat debug                    : builds debug build
@@ -811,6 +815,7 @@ echo   vcbuild.bat enable-vtune             : builds Node.js with Intel VTune pr
 echo   vcbuild.bat link-module my_module.js : bundles my_module as built-in module
 echo   vcbuild.bat lint                     : runs the C++, documentation and JavaScript linter
 echo   vcbuild.bat no-cctest                : skip building cctest.exe
+echo   vcbuild.bat ccache c:\ccache\        : use ccache to speed build
 goto exit
 
 :exit