summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Modules/CMakeDetermineCompilerABI.cmake35
-rw-r--r--Source/CMakeLists.txt2
-rw-r--r--Source/CMakeVersion.cmake2
-rw-r--r--Source/cmFileCommand.cxx2
-rw-r--r--Source/cmFileCommand_ReadMacho.cxx99
-rw-r--r--Source/cmFileCommand_ReadMacho.h11
-rw-r--r--Source/cmFileSet.cxx36
-rw-r--r--Source/cmMachO.cxx55
-rw-r--r--Source/cmMachO.h22
-rw-r--r--Tests/FindMatlab/no_implicit_link_checks/CMakeLists.txt2
-rwxr-xr-xbootstrap11
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(
diff --git a/bootstrap b/bootstrap
index 9958a6d..49b91d5 100755
--- a/bootstrap
+++ b/bootstrap
@@ -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"