diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx index 9b9a3f86d0..b50f0a01f4 100644 --- a/Source/cmMakefile.cxx +++ b/Source/cmMakefile.cxx @@ -3511,6 +3511,27 @@ std::string cmMakefile::GetModulesFile(cm::string_view filename, bool& system, result = moduleInCMakeRoot; } +#if defined(_WIN32) || defined(__APPLE__) + if (!result.empty()) { + std::string const requestedName = + cmSystemTools::GetFilenameName(std::string{ filename }); + std::string actualName; + cmsys::Status const status = + cmSystemTools::ReadNameOnDisk(result, actualName); + if (status && actualName != requestedName) { + this->IssueDiagnostic( + cmDiagnostics::CMD_AUTHOR, + cmStrCat("The module name\n ", requestedName, '\n', + "does not match the case of the module file name on disk\n" + " ", + cmSystemTools::GetFilenamePath(result), '/', actualName, '\n', + "This may fail on case-sensitive file systems. " + "Use the module name\n ", + actualName, "\ninstead.")); + } + } +#endif + return result; } diff --git a/Source/cmSystemTools.cxx b/Source/cmSystemTools.cxx index 485a72eb50..41e6c95557 100644 --- a/Source/cmSystemTools.cxx +++ b/Source/cmSystemTools.cxx @@ -367,6 +367,14 @@ RealSystem RealOS; } // namespace +#if defined(_WIN32) || defined(__APPLE__) +cmsys::Status cmSystemTools::ReadNameOnDisk(std::string const& path, + std::string& name) +{ + return ::ReadNameOnDisk(path, name); +} +#endif + #if !defined(HAVE_ENVIRON_NOT_REQUIRE_PROTOTYPE) // For GetEnvironmentVariables # if defined(_WIN32) diff --git a/Source/cmSystemTools.h b/Source/cmSystemTools.h index 7eaee0ec5c..31bdf62222 100644 --- a/Source/cmSystemTools.h +++ b/Source/cmSystemTools.h @@ -672,6 +672,12 @@ public: /** Return subview of the full filename (i.e. file name without path) */ static cm::string_view GetFilenameNameView(cm::string_view filename); +#if defined(_WIN32) || defined(__APPLE__) + /** Read the on-disk spelling of the full filename's last component. */ + static cmsys::Status ReadNameOnDisk(std::string const& path, + std::string& name); +#endif + /** * Return subview of file extension of a full filename (dot included). * Warning: this is the shortest extension (for example: .gz of .tar.gz) diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt index 441e4de823..8020d96833 100644 --- a/Tests/CMakeLists.txt +++ b/Tests/CMakeLists.txt @@ -264,6 +264,22 @@ if(BUILD_TESTING) unset(_sysctl_stdout) endif() + if(NOT DEFINED CMake_TEST_FILESYSTEM_CASE) + if(CMAKE_HOST_WIN32) + set(CMake_TEST_FILESYSTEM_CASE 1) + elseif(CMAKE_HOST_APPLE) + execute_process( + COMMAND sw_vers -productVersion + OUTPUT_VARIABLE sw_vers_productVersion + OUTPUT_STRIP_TRAILING_WHITESPACE + RESULT_VARIABLE _sw_vers_result + ) + if(sw_vers_productVersion VERSION_GREATER_EQUAL 10.11) + set(CMake_TEST_FILESYSTEM_CASE 1) + endif() + endif() + endif() + if(CMake_TEST_CUDA AND NOT CMake_TEST_CUDA MATCHES "^(NVIDIA|Clang)$") message(FATAL_ERROR "CMake_TEST_CUDA should be \"NVIDIA\" or \"Clang\", got \"${CMake_TEST_CUDA}\"") endif() diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt index 1e0fc40652..cdfd0aec16 100644 --- a/Tests/RunCMake/CMakeLists.txt +++ b/Tests/RunCMake/CMakeLists.txt @@ -777,7 +777,8 @@ add_RunCMake_test(file-RPATH add_RunCMake_test(file-STRINGS) add_RunCMake_test(find_file -DMINGW=${MINGW}) add_RunCMake_test(find_library -DMINGW=${MINGW} -DCYGWIN=${CYGWIN} -DMSYS=${MSYS} -DMSVC=${MSVC}) -add_RunCMake_test(find_package -DMINGW=${MINGW} -DMSYS=${MSYS}) +add_RunCMake_test(find_package -DMINGW=${MINGW} -DMSYS=${MSYS} + -DCMake_TEST_FILESYSTEM_CASE=${CMake_TEST_FILESYSTEM_CASE}) add_RunCMake_test(find_package-CPS) add_RunCMake_test(find_path -DMINGW=${MINGW}) add_RunCMake_test(find_program -DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME}) @@ -792,7 +793,7 @@ endif() add_RunCMake_test(INSTALL_OBJECT_NAME_STRATEGY) add_RunCMake_test(INSTALL_OBJECT_ONLY_USE_DESTINATION) add_RunCMake_test(if -DMSYS=${MSYS}) -add_RunCMake_test(include) +add_RunCMake_test(include -DCMake_TEST_FILESYSTEM_CASE=${CMake_TEST_FILESYSTEM_CASE}) add_RunCMake_test(include_directories) add_RunCMake_test(include_guard) add_RunCMake_test(list) diff --git a/Tests/RunCMake/find_package/ModuleNameCaseMismatch-stderr.txt b/Tests/RunCMake/find_package/ModuleNameCaseMismatch-stderr.txt new file mode 100644 index 0000000000..c3b26e83bb --- /dev/null +++ b/Tests/RunCMake/find_package/ModuleNameCaseMismatch-stderr.txt @@ -0,0 +1,18 @@ +^CMake Warning \(author\) at ModuleNameCaseMismatch\.cmake:[0-9]+ \(find_package\): + The module name + + Findcasemodule\.cmake + + does not match the case of the module file name on disk + + [^ +]*/ModuleNameCaseMismatch/FindCaseModule\.cmake + + This may fail on case-sensitive file systems\. Use the module name + + FindCaseModule\.cmake + + instead\. +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author to suppress it\.$ diff --git a/Tests/RunCMake/find_package/ModuleNameCaseMismatch.cmake b/Tests/RunCMake/find_package/ModuleNameCaseMismatch.cmake new file mode 100644 index 0000000000..d3fae60ad1 --- /dev/null +++ b/Tests/RunCMake/find_package/ModuleNameCaseMismatch.cmake @@ -0,0 +1,7 @@ +list(PREPEND CMAKE_MODULE_PATH + "${CMAKE_CURRENT_LIST_DIR}/ModuleNameCaseMismatch") +find_package(casemodule MODULE) + +if(NOT casemodule_FOUND) + message(FATAL_ERROR "FindCaseModule.cmake not included") +endif() diff --git a/Tests/RunCMake/find_package/ModuleNameCaseMismatch/FindCaseModule.cmake b/Tests/RunCMake/find_package/ModuleNameCaseMismatch/FindCaseModule.cmake new file mode 100644 index 0000000000..91a518242f --- /dev/null +++ b/Tests/RunCMake/find_package/ModuleNameCaseMismatch/FindCaseModule.cmake @@ -0,0 +1 @@ +set(casemodule_FOUND TRUE) diff --git a/Tests/RunCMake/find_package/RunCMakeTest.cmake b/Tests/RunCMake/find_package/RunCMakeTest.cmake index 1d96eeb745..589eae5ab8 100644 --- a/Tests/RunCMake/find_package/RunCMakeTest.cmake +++ b/Tests/RunCMake/find_package/RunCMakeTest.cmake @@ -35,6 +35,9 @@ run_cmake(MissingConfigOneName) run_cmake(MissingConfigRequired) run_cmake(MissingConfigVersion) run_cmake(MixedModeOptions) +if(CMake_TEST_FILESYSTEM_CASE) + run_cmake(ModuleNameCaseMismatch) +endif() run_cmake_with_options(ModuleModeDebugPkg --debug-find-pkg=Foo,Zot) run_cmake(NestedConfig) run_cmake(PackageRoot) diff --git a/Tests/RunCMake/include/CaseMismatch-stderr.txt b/Tests/RunCMake/include/CaseMismatch-stderr.txt new file mode 100644 index 0000000000..1fb4d8a2ee --- /dev/null +++ b/Tests/RunCMake/include/CaseMismatch-stderr.txt @@ -0,0 +1,18 @@ +^CMake Warning \(author\) at CaseMismatch\.cmake:[0-9]+ \(include\): + The module name + + casemodule\.cmake + + does not match the case of the module file name on disk + + [^ +]*/CaseMismatch/CaseModule\.cmake + + This may fail on case-sensitive file systems\. Use the module name + + CaseModule\.cmake + + instead\. +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author to suppress it\.$ diff --git a/Tests/RunCMake/include/CaseMismatch.cmake b/Tests/RunCMake/include/CaseMismatch.cmake new file mode 100644 index 0000000000..54af68c886 --- /dev/null +++ b/Tests/RunCMake/include/CaseMismatch.cmake @@ -0,0 +1,6 @@ +list(PREPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/CaseMismatch") +include(casemodule) + +if(NOT case_module_included) + message(FATAL_ERROR "CaseModule.cmake not included") +endif() diff --git a/Tests/RunCMake/include/CaseMismatch/CaseModule.cmake b/Tests/RunCMake/include/CaseMismatch/CaseModule.cmake new file mode 100644 index 0000000000..cf13bde260 --- /dev/null +++ b/Tests/RunCMake/include/CaseMismatch/CaseModule.cmake @@ -0,0 +1 @@ +set(case_module_included TRUE) diff --git a/Tests/RunCMake/include/RunCMakeTest.cmake b/Tests/RunCMake/include/RunCMakeTest.cmake index cd5eedbe56..921ebd49a5 100644 --- a/Tests/RunCMake/include/RunCMakeTest.cmake +++ b/Tests/RunCMake/include/RunCMakeTest.cmake @@ -6,6 +6,9 @@ run_cmake(CMP0024-NEW) run_cmake(ExportExportInclude) run_cmake(IncludeIsDirectory) run_cmake(IncludeMalformed) +if(CMake_TEST_FILESYSTEM_CASE) + run_cmake(CaseMismatch) +endif() run_cmake(ParentVariableRoot) run_cmake(ParentVariableSubDir) run_cmake_script(ParentVariableScript)