diff options
-rw-r--r-- | Modules/CMakeDetermineCompilerABI.cmake | 35 | ||||
-rw-r--r-- | Source/CMakeLists.txt | 2 | ||||
-rw-r--r-- | Source/CMakeVersion.cmake | 2 | ||||
-rw-r--r-- | Source/cmFileCommand.cxx | 2 | ||||
-rw-r--r-- | Source/cmFileCommand_ReadMacho.cxx | 99 | ||||
-rw-r--r-- | Source/cmFileCommand_ReadMacho.h | 11 | ||||
-rw-r--r-- | Source/cmFileSet.cxx | 36 | ||||
-rw-r--r-- | Source/cmMachO.cxx | 55 | ||||
-rw-r--r-- | Source/cmMachO.h | 22 | ||||
-rw-r--r-- | Tests/FindMatlab/no_implicit_link_checks/CMakeLists.txt | 2 | ||||
-rwxr-xr-x | bootstrap | 11 |
11 files changed, 266 insertions, 11 deletions
diff --git a/Modules/CMakeDetermineCompilerABI.cmake b/Modules/CMakeDetermineCompilerABI.cmake index 4049bc1..671a0b6 100644 --- a/Modules/CMakeDetermineCompilerABI.cmake +++ b/Modules/CMakeDetermineCompilerABI.cmake @@ -99,6 +99,41 @@ function(CMAKE_DETERMINE_COMPILER_ABI lang src) # Load the resulting information strings. if(CMAKE_${lang}_ABI_COMPILED) message(CHECK_PASS "done") + if(CMAKE_HOST_APPLE AND CMAKE_SYSTEM_NAME STREQUAL "Darwin") + file(READ_MACHO "${BIN}" ARCHITECTURES archs CAPTURE_ERROR macho_error) # undocumented file() subcommand + if (NOT macho_error) + # sort and prune the list of found architectures + set(arch_list_sorted ${archs}) + list(SORT arch_list_sorted) + list(REMOVE_DUPLICATES arch_list_sorted) + # sort and prune the list of requested architectures + set(requested_arch_list ${CMAKE_OSX_ARCHITECTURES}) + list(SORT requested_arch_list) + list(REMOVE_DUPLICATES requested_arch_list) + message(CONFIGURE_LOG + "Effective list of requested architectures (possibly empty) : \"${requested_arch_list}\"\n" + "Effective list of architectures found in the ABI info binary: \"${arch_list_sorted}\"\n") + # If all generated architectures were known to READ_MACHO (i.e. libmacho): + # Compare requested and found: + # - if no architecture(s) were requested explicitly, just check if READ_MACHO returned + # an architecture for the ABI info binary. + # - otherwise, check if the requested and found lists are equal + if(arch_list_sorted MATCHES "unknown") + # Rare but not impossible: a host with a toolchain capable of generating binaries with + # architectures that the system libmacho is too old to know. Report the found archs as + # usual, warn about the unknowns and skip the comparison with CMAKE_OSX_ARCHITECTURES. + message(WARNING "The ${lang} compiler generates universal binaries with at least 1 architecture not known to the host") + elseif(requested_arch_list AND arch_list_sorted + AND NOT "${requested_arch_list}" STREQUAL "${arch_list_sorted}") + # inform the user of the mismatch but show the raw input and output lists + message(FATAL_ERROR + "The ${lang} compiler targets architectures:\n" + " \"${archs}\"\n" + "but CMAKE_OSX_ARCHITECTURES is\n" + " \"${CMAKE_OSX_ARCHITECTURES}\"\n") + endif() + endif() + endif() cmake_policy(PUSH) cmake_policy(SET CMP0159 NEW) # file(STRINGS) with REGEX updates CMAKE_MATCH_<n> file(STRINGS "${BIN}" ABI_STRINGS LIMIT_COUNT 32 REGEX "INFO:[A-Za-z0-9_]+\\[[^]]*\\]") diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index 2bd3bdd..185ebfe 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -557,6 +557,8 @@ add_library( cmFLTKWrapUICommand.h cmFileCommand.cxx cmFileCommand.h + cmFileCommand_ReadMacho.cxx + cmFileCommand_ReadMacho.h cmFindBase.cxx cmFindBase.h cmFindCommon.cxx diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake index f9df02e..d638a9d 100644 --- a/Source/CMakeVersion.cmake +++ b/Source/CMakeVersion.cmake @@ -1,7 +1,7 @@ # CMake version number components. set(CMake_VERSION_MAJOR 3) set(CMake_VERSION_MINOR 29) -set(CMake_VERSION_PATCH 20240604) +set(CMake_VERSION_PATCH 20240605) #set(CMake_VERSION_RC 0) set(CMake_VERSION_IS_DIRTY 0) diff --git a/Source/cmFileCommand.cxx b/Source/cmFileCommand.cxx index 77f740b..ce8cc2a 100644 --- a/Source/cmFileCommand.cxx +++ b/Source/cmFileCommand.cxx @@ -35,6 +35,7 @@ #include "cmELF.h" #include "cmExecutionStatus.h" #include "cmFSPermissions.h" +#include "cmFileCommand_ReadMacho.h" #include "cmFileCopier.h" #include "cmFileInstaller.h" #include "cmFileLockPool.h" @@ -3965,6 +3966,7 @@ bool cmFileCommand(std::vector<std::string> const& args, { "RPATH_CHECK"_s, HandleRPathCheckCommand }, { "RPATH_REMOVE"_s, HandleRPathRemoveCommand }, { "READ_ELF"_s, HandleReadElfCommand }, + { "READ_MACHO"_s, HandleReadMachoCommand }, { "REAL_PATH"_s, HandleRealPathCommand }, { "RELATIVE_PATH"_s, HandleRelativePathCommand }, { "TO_CMAKE_PATH"_s, HandleCMakePathCommand }, diff --git a/Source/cmFileCommand_ReadMacho.cxx b/Source/cmFileCommand_ReadMacho.cxx new file mode 100644 index 0000000..870b3ca --- /dev/null +++ b/Source/cmFileCommand_ReadMacho.cxx @@ -0,0 +1,99 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmFileCommand_ReadMacho.h" + +#include "cmArgumentParser.h" +#include "cmExecutionStatus.h" +#include "cmMakefile.h" +#include "cmRange.h" +#include "cmStringAlgorithms.h" +#include "cmSystemTools.h" +#if defined(CMake_USE_MACH_PARSER) +# include "cmMachO.h" +#endif + +#include <cmext/string_view> + +bool HandleReadMachoCommand(std::vector<std::string> const& args, + cmExecutionStatus& status) +{ + if (args.size() < 4) { + status.SetError("READ_MACHO must be called with at least three additional " + "arguments."); + return false; + } + + std::string const& fileNameArg = args[1]; + + struct Arguments + { + std::string Architectures; + std::string Error; + }; + + static auto const parser = + cmArgumentParser<Arguments>{} + .Bind("ARCHITECTURES"_s, &Arguments::Architectures) + .Bind("CAPTURE_ERROR"_s, &Arguments::Error); + Arguments const arguments = parser.Parse(cmMakeRange(args).advance(2), + /*unparsedArguments=*/nullptr); + + if (!arguments.Architectures.empty()) { + // always return something sensible for ARCHITECTURES + status.GetMakefile().AddDefinition(arguments.Architectures, "unknown"_s); + } + if (!cmSystemTools::FileExists(fileNameArg, true)) { + if (arguments.Error.empty()) { + status.SetError(cmStrCat("READ_MACHO given FILE \"", fileNameArg, + "\" that does not exist.")); + return false; + } + status.GetMakefile().AddDefinition( + arguments.Error, cmStrCat(fileNameArg, " does not exist")); + return true; + } + +#if defined(CMake_USE_MACH_PARSER) + cmMachO macho(fileNameArg.c_str()); + if (!macho) { + if (arguments.Error.empty()) { + status.SetError(cmStrCat("READ_MACHO given FILE:\n ", fileNameArg, + "\nthat is not a valid Macho-O file.")); + return false; + } + status.GetMakefile().AddDefinition( + arguments.Error, cmStrCat(fileNameArg, " is not a valid Macho-O file")); + return true; + } else if (!macho.GetErrorMessage().empty()) { + if (arguments.Error.empty()) { + status.SetError(cmStrCat( + "READ_MACHO given FILE:\n ", fileNameArg, + "\nthat is not a supported Macho-O file: ", macho.GetErrorMessage())); + return false; + } + status.GetMakefile().AddDefinition( + arguments.Error, + cmStrCat(fileNameArg, + " is not a supported Macho-O file: ", macho.GetErrorMessage())); + return true; + } + + std::string output; + + if (!arguments.Architectures.empty()) { + auto archs = macho.GetArchitectures(); + output = cmJoin(archs, ";"); + + // Save the output in a makefile variable. + status.GetMakefile().AddDefinition(arguments.Architectures, output); + } +#else + if (arguments.Error.empty()) { + status.SetError("READ_MACHO support not available on this platform."); + return false; + } + status.GetMakefile().AddDefinition( + arguments.Error, "READ_MACHO support not available on this platform."); +#endif // CMake_USE_MACH_PARSER + return true; +} diff --git a/Source/cmFileCommand_ReadMacho.h b/Source/cmFileCommand_ReadMacho.h new file mode 100644 index 0000000..b79e15f --- /dev/null +++ b/Source/cmFileCommand_ReadMacho.h @@ -0,0 +1,11 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#pragma once + +#include <string> +#include <vector> + +class cmExecutionStatus; + +bool HandleReadMachoCommand(std::vector<std::string> const& args, + cmExecutionStatus& status); diff --git a/Source/cmFileSet.cxx b/Source/cmFileSet.cxx index b74855f..a00c10e 100644 --- a/Source/cmFileSet.cxx +++ b/Source/cmFileSet.cxx @@ -4,9 +4,11 @@ #include <sstream> #include <string> +#include <unordered_map> #include <utility> #include <vector> +#include <cm/optional> #include <cmext/algorithm> #include <cmext/string_view> @@ -158,6 +160,13 @@ std::vector<std::string> cmFileSet::EvaluateDirectoryEntries( const cmGeneratorTarget* target, cmGeneratorExpressionDAGChecker* dagChecker) const { + struct DirCacheEntry + { + std::string collapsedDir; + cm::optional<cmSystemTools::FileId> fileId; + }; + + std::unordered_map<std::string, DirCacheEntry> dirCache; std::vector<std::string> result; for (auto const& cge : cges) { auto entry = cge->Evaluate(lg, config, target, dagChecker); @@ -166,12 +175,29 @@ std::vector<std::string> cmFileSet::EvaluateDirectoryEntries( if (!cmSystemTools::FileIsFullPath(dir)) { dir = cmStrCat(lg->GetCurrentSourceDirectory(), '/', dir); } - auto collapsedDir = cmSystemTools::CollapseFullPath(dir); + + auto dirCacheResult = dirCache.emplace(dir, DirCacheEntry()); + auto& dirCacheEntry = dirCacheResult.first->second; + const auto isNewCacheEntry = dirCacheResult.second; + + if (isNewCacheEntry) { + cmSystemTools::FileId fileId; + auto isFileIdValid = cmSystemTools::GetFileId(dir, fileId); + dirCacheEntry.collapsedDir = cmSystemTools::CollapseFullPath(dir); + dirCacheEntry.fileId = + isFileIdValid ? cm::optional<decltype(fileId)>(fileId) : cm::nullopt; + } + for (auto const& priorDir : result) { - auto collapsedPriorDir = cmSystemTools::CollapseFullPath(priorDir); - if (!cmSystemTools::SameFile(collapsedDir, collapsedPriorDir) && - (cmSystemTools::IsSubDirectory(collapsedDir, collapsedPriorDir) || - cmSystemTools::IsSubDirectory(collapsedPriorDir, collapsedDir))) { + auto priorDirCacheEntry = dirCache.at(priorDir); + bool sameFile = dirCacheEntry.fileId.has_value() && + priorDirCacheEntry.fileId.has_value() && + (*dirCacheEntry.fileId == *priorDirCacheEntry.fileId); + if (!sameFile && + (cmSystemTools::IsSubDirectory(dirCacheEntry.collapsedDir, + priorDirCacheEntry.collapsedDir) || + cmSystemTools::IsSubDirectory(priorDirCacheEntry.collapsedDir, + dirCacheEntry.collapsedDir))) { lg->GetCMakeInstance()->IssueMessage( MessageType::FATAL_ERROR, cmStrCat( diff --git a/Source/cmMachO.cxx b/Source/cmMachO.cxx index 91a7b84..48d2453 100644 --- a/Source/cmMachO.cxx +++ b/Source/cmMachO.cxx @@ -4,7 +4,6 @@ #include <cstddef> #include <string> -#include <vector> #include <cm/memory> @@ -13,8 +12,12 @@ #include "cmAlgorithms.h" // Include the Mach-O format information system header. +#include <mach-o/arch.h> #include <mach-o/fat.h> #include <mach-o/loader.h> +#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 130000 +# include <mach-o/utils.h> +#endif /** @@ -115,12 +118,15 @@ public: return v; } + struct cmMachO::MachHeader mach_header() const { return MachHeader; } + protected: bool read_load_commands(uint32_t ncmds, uint32_t sizeofcmds, cmsys::ifstream& fin); bool Swap; std::vector<RawLoadCommand> LoadCommands; + struct cmMachO::MachHeader MachHeader; }; // Implementation for reading Mach-O header and load commands. @@ -138,9 +144,11 @@ public: if (!read(fin, this->Header)) { return false; } - this->Header.cputype = swap(this->Header.cputype); - this->Header.cpusubtype = swap(this->Header.cpusubtype); - this->Header.filetype = swap(this->Header.filetype); + // swap the header data and export a (potentially) useful subset via the + // parent class. + this->MachHeader.CpuType = swap(this->Header.cputype); + this->MachHeader.CpuSubType = swap(this->Header.cpusubtype); + this->MachHeader.FileType = swap(this->Header.filetype); this->Header.ncmds = swap(this->Header.ncmds); this->Header.sizeofcmds = swap(this->Header.sizeofcmds); this->Header.flags = swap(this->Header.flags); @@ -311,6 +319,9 @@ bool cmMachOInternal::read_mach_o(uint32_t file_offset) cmMachO::cmMachO(const char* fname) : Internal(cm::make_unique<cmMachOInternal>(fname)) { + for (const auto& m : this->Internal->MachOList) { + Headers.push_back(m->mach_header()); + } } cmMachO::~cmMachO() = default; @@ -355,3 +366,39 @@ bool cmMachO::GetInstallName(std::string& install_name) void cmMachO::PrintInfo(std::ostream& /*os*/) const { } + +cmMachO::StringList cmMachO::GetArchitectures() const +{ + cmMachO::StringList archs; + if (Valid() && !this->Headers.empty()) { + for (const auto& header : this->Headers) { + const char* archName = "unknown"; +#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 130000 + if (__builtin_available(macOS 13.0, *)) { + archName = (header.CpuType & CPU_TYPE_ARM) + ? macho_arch_name_for_cpu_type(header.CpuType, header.CpuSubType) + : macho_arch_name_for_cpu_type(header.CpuType, CPU_SUBTYPE_MULTIPLE); + } else +#endif + { +#if defined __clang__ +# define CM_MACOS_DEPRECATED_NXGetArchInfoFromCpuType +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wdeprecated-declarations" +#endif + const NXArchInfo* archInfo = (header.CpuType & CPU_TYPE_ARM) + ? NXGetArchInfoFromCpuType(header.CpuType, header.CpuSubType) + : NXGetArchInfoFromCpuType(header.CpuType, CPU_SUBTYPE_MULTIPLE); +#ifdef CM_MACOS_DEPRECATED_NXGetArchInfoFromCpuType +# undef CM_MACOS_DEPRECATED_NXGetArchInfoFromCpuType +# pragma clang diagnostic pop +#endif + if (archInfo) { + archName = archInfo->name; + } + } + archs.push_back(archName); + } + } + return archs; +} diff --git a/Source/cmMachO.h b/Source/cmMachO.h index ec7d54c..ddbfc28 100644 --- a/Source/cmMachO.h +++ b/Source/cmMachO.h @@ -7,6 +7,9 @@ #include <iosfwd> #include <memory> #include <string> +#include <vector> + +#include <mach/machine.h> #if !defined(CMake_USE_MACH_PARSER) # error "This file may be included only if CMake_USE_MACH_PARSER is enabled." @@ -20,6 +23,16 @@ class cmMachOInternal; class cmMachO { public: + struct MachHeader + { + cpu_type_t CpuType; + cpu_subtype_t CpuSubType; + uint32_t FileType; + }; + class StringList : public std::vector<std::string> + { + }; + /** Construct with the name of the Mach-O input file to parse. */ cmMachO(const char* fname); @@ -38,8 +51,17 @@ public: /** Print human-readable information about the Mach-O file. */ void PrintInfo(std::ostream& os) const; + /** Get the architectural header(s) from the Mach-O file. */ + std::vector<struct MachHeader> GetHeaders() const { return this->Headers; } + + /** Get a list of the recognized architectures present in the Mach-O file + * in the order in which they are found. + */ + StringList GetArchitectures() const; + private: friend class cmMachOInternal; bool Valid() const; std::unique_ptr<cmMachOInternal> Internal; + std::vector<struct MachHeader> Headers; }; diff --git a/Tests/FindMatlab/no_implicit_link_checks/CMakeLists.txt b/Tests/FindMatlab/no_implicit_link_checks/CMakeLists.txt index 58db0ec..7e1bbae 100644 --- a/Tests/FindMatlab/no_implicit_link_checks/CMakeLists.txt +++ b/Tests/FindMatlab/no_implicit_link_checks/CMakeLists.txt @@ -58,7 +58,7 @@ endif() # Link separately with Matlab::mx and Matlab::mex to ensure that compilation # and run of the test is successful -target_link_libraries(cmake_matlab_test_wrapper1 PRIVATE Matlab::mx Matlab::mex) +target_link_libraries(cmake_matlab_test_wrapper1 Matlab::mx Matlab::mex) if(RUN_UNIT_TESTS) matlab_add_unit_test( @@ -353,6 +353,7 @@ CMAKE_CXX_SOURCES="\ cmExprParserHelper \ cmExternalMakefileProjectGenerator \ cmFileCommand \ + cmFileCommand_ReadMacho \ cmFileCopier \ cmFileInstaller \ cmFileSet \ @@ -521,6 +522,12 @@ CMAKE_CXX_SOURCES="\ cm_fileno \ " +if ${cmake_system_darwin}; then + CMAKE_CXX_SOURCES="${CMAKE_CXX_SOURCES}\ + cmMachO \ + " +fi + if ${cmake_system_mingw}; then CMAKE_CXX_SOURCES="${CMAKE_CXX_SOURCES}\ cmGlobalMSYSMakefileGenerator \ @@ -1669,6 +1676,10 @@ else cmake_report cmConfigure.h${_tmp} "#define CMAKE_BOOTSTRAP_MAKEFILES" fi +if ${cmake_system_darwin}; then + cmake_report cmConfigure.h${_tmp} "#define CMake_USE_MACH_PARSER" +fi + if ${cmake_system_mingw}; then cmake_report cmConfigure.h${_tmp} "#if defined(_WIN32) && !defined(NOMINMAX)" cmake_report cmConfigure.h${_tmp} "# define NOMINMAX" |