From 752dd035c1fa6353b2c44ea1e02cbb40eb62a54f Mon Sep 17 00:00:00 2001 From: David Roberts Date: Wed, 13 Jan 2021 13:00:32 +0000 Subject: [PATCH] [7.x][ML] Changes to allow native compilation on ARM Macs (#1664) This allows native compilation on a Mac containing the Apple M1 CPU. PR builds will currently NOT test this platform, nor even check compilation for it. Release manager will not build it either. Still, it is a step in the right direction to be able to compile locally for it. Cross compilation for this platform is still NOT possible. This can be investigated in a followup PR. (Supporting this actually requires considerable changes to the cross compilation framework, as it currently contains a quite deeply embedded assumption that macOS is x86_64 only.) The last dregs of Linux MUSL port code are also removed. (Most support for MUSL was removed previously, but a few bits were missed.) Backport of #1651 --- 3rd_party/3rd_party.sh | 7 ++++- build-setup/macos.md | 38 ++++++++++++++++++++++-- build.gradle | 20 ++++++------- include/api/CInferenceModelMetadata.h | 2 +- lib/core/CUname.cc | 20 +++++-------- lib/maths/unittest/CBoostedTreeTest.cc | 2 +- lib/maths/unittest/CLinearAlgebraTest.cc | 2 +- mk/macosx.mk | 36 +++++++++++++++------- set_env.sh | 10 +------ 9 files changed, 87 insertions(+), 50 deletions(-) diff --git a/3rd_party/3rd_party.sh b/3rd_party/3rd_party.sh index 933597ad31..db635747cf 100755 --- a/3rd_party/3rd_party.sh +++ b/3rd_party/3rd_party.sh @@ -37,7 +37,12 @@ case `uname` in Darwin) BOOST_LOCATION=/usr/local/lib BOOST_COMPILER=clang - BOOST_EXTENSION=mt-x64-1_71.dylib + if [ `uname -m` = x86_64 ] ; then + BOOST_ARCH=x64 + else + BOOST_ARCH=a64 + fi + BOOST_EXTENSION=mt-${BOOST_ARCH}-1_71.dylib BOOST_LIBRARIES='atomic chrono date_time filesystem iostreams log log_setup program_options regex system thread' XML_LOCATION= GCC_RT_LOCATION= diff --git a/build-setup/macos.md b/build-setup/macos.md index 2c6b87a117..3509798938 100644 --- a/build-setup/macos.md +++ b/build-setup/macos.md @@ -1,5 +1,7 @@ # Machine Learning Build Machine Setup for macOS +These same instructions should work for native compilation on both x86_64 and aarch64 architectures. + To ensure everything is consistent for redistributable builds we build all redistributable components from source. NOTE: if you upgrade macOS then Xcode may need to be re-installed. Please ensure your Xcode is still valid after upgrades. @@ -27,11 +29,12 @@ Note, that bash doesn't read `~/.bashrc` for login shells (which is what you get Most of the tools are built via a GNU "configure" script. There are some environment variables that affect the behaviour of this. Therefore, when building ANY tool on macOS, set the following environment variables: ``` +export SSEFLAGS=`[ $(uname -m) = x86_64 ] && echo -msse4.2` export CPP='clang -E' export CC=clang -export CFLAGS='-O3 -msse4.2' +export CFLAGS="-O3 $SSEFLAGS" export CXX='clang++ -std=c++14 -stdlib=libc++' -export CXXFLAGS='-O3 -msse4.2' +export CXXFLAGS="-O3 $SSEFLAGS" export CXXCPP='clang++ -std=c++14 -E' export LDFLAGS=-Wl,-headerpad_max_install_names unset CPATH @@ -50,7 +53,8 @@ The first major piece of development software to install is Apple's development - If you are using El Capitan, you must install Xcode 8.2.x - If you are using Sierra, you must install Xcode 9.2.x - If you are using High Sierra, you must install Xcode 10.1.x -- If you are using Mojave, you must install Xcode 10.3.x +- If you are using Mojave, you must install Xcode 11.3.x +- If you are using Catalina or Big Sur, you must install Xcode 12.3.x or above Older versions of Xcode are installed by dragging the app from the `.dmg` file to the `/Applications` directory on your Mac (or if you got it from the App Store it will already be in the `/Applications` directory). More modern versions of Xcode are distributed as a `.xip` file; simply double click the `.xip` file to expand it, then drag `Xcode.app` to your `/Applications` directory. @@ -92,6 +96,34 @@ to: (3ul)(17ul)(29ul)(37ul)(53ul)(67ul)(79ul) \ ``` +Then edit `tools/build/src/tools/darwin.jam` and change: + +``` + case arm : + { + if $(instruction-set) { + options = -arch$(_)$(instruction-set) ; + } else { + options = -arch arm ; + } + } +``` + +to: + +``` + case arm : + { + if $(instruction-set) { + options = -arch$(_)$(instruction-set) ; + } else if $(address-model) = 64 { + options = -arch arm64 ; + } else { + options = -arch arm ; + } + } +``` + To complete the build, type: ``` diff --git a/build.gradle b/build.gradle index 32c736951c..e8dbbaae65 100644 --- a/build.gradle +++ b/build.gradle @@ -37,28 +37,28 @@ if (cppCrossCompile != '' && cppCrossCompile != 'macosx' && cppCrossCompile != ' throw new GradleException("CPP_CROSS_COMPILE property must be empty, 'macosx' or 'aarch64'") } +String osArch = System.properties['os.arch'] +// Some versions of Java report hardware architectures that +// don't match other tools - these need to be normalized +if (osArch == 'amd64') { + osArch = 'x86_64' +} else if (osArch == 'arm64') { + osArch = 'aarch64' +} String artifactClassifier; if (isWindows) { artifactClassifier = 'windows-x86_64' } else if (isMacOsX || cppCrossCompile == 'macosx') { - artifactClassifier = 'darwin-x86_64' + artifactClassifier = 'darwin-' + osArch } else if (cppCrossCompile != '') { artifactClassifier = 'linux-' + cppCrossCompile } else { - String osArch = System.properties['os.arch'] - // Some versions of Java report hardware architectures that - // don't match other tools - these need to be normalized - if (osArch == 'amd64') { - osArch = 'x86_64' - } else if (osArch == 'i386') { - osArch = 'x86' - } artifactClassifier = 'linux-' + osArch } // Always do the C++ build using bash (Git bash on Windows) project.ext.bash = isWindows ? "C:\\Program Files\\Git\\bin\\bash" : "/bin/bash" -project.ext.make = (isMacOsX || isWindows) ? "gnumake" : (isLinux ? "make" : "gmake") +project.ext.make = (isMacOsX || isWindows) ? "gnumake" : "make" project.ext.numCpus = Runtime.runtime.availableProcessors() project.ext.makeEnvironment = [ 'CPP_CROSS_COMPILE': cppCrossCompile, 'VERSION_QUALIFIER': versionQualifier, diff --git a/include/api/CInferenceModelMetadata.h b/include/api/CInferenceModelMetadata.h index 2539d5db89..6ac857d0a1 100644 --- a/include/api/CInferenceModelMetadata.h +++ b/include/api/CInferenceModelMetadata.h @@ -74,7 +74,7 @@ class API_EXPORT CInferenceModelMetadata { bool supplied) : s_HyperparameterName(hyperparameterName), s_Value(value), s_AbsoluteImportance(absoluteImportance), - s_RelativeImportance(relativeImportance), s_Supplied(supplied){}; + s_RelativeImportance(relativeImportance), s_Supplied(supplied) {} std::string s_HyperparameterName; double s_Value; double s_AbsoluteImportance; diff --git a/lib/core/CUname.cc b/lib/core/CUname.cc index 47b07f3ec8..43fdb603ea 100644 --- a/lib/core/CUname.cc +++ b/lib/core/CUname.cc @@ -76,28 +76,18 @@ std::string CUname::mlPlatform() { // downloads. For *nix platforms this is more-or-less `uname -s`-`uname -m` // converted to lower case. However, for consistency between different // operating systems on the same architecture "amd64"/"i86pc" are replaced - // with "x86_64" and "i386"/"i686" with "x86". + // with "x86_64", "i386"/"i686" with "x86" and "arm64" with "aarch64". // // Assuming the current platform is supported this name will be one of: + // - darwin-aarch64 // - darwin-x86_64 + // - linux-aarch64 // - linux-x86_64 // // For an unsupported platform it will be something different, but hopefully // still recognisable. std::string os(CStringUtils::toLower(name.sysname)); -#ifdef _CS_GNU_LIBC_VERSION - if (os == "linux") { - char buffer[128] = {'\0'}; - // This isn't great because it's assuming that any C runtime library - // that doesn't identify itself as glibc is musl, but it's hard to do - // better as musl goes out of its way to be hard to detect - if (::confstr(_CS_GNU_LIBC_VERSION, buffer, sizeof(buffer)) == 0 || - ::strstr(buffer, "glibc") == 0) { - os += "-musl"; - } - } -#endif const std::string& machine = CStringUtils::toLower(name.machine); if (machine.length() == 4 && machine[0] == 'i' && machine[2] == '8' && @@ -109,6 +99,10 @@ std::string CUname::mlPlatform() { return os + "-x86_64"; } + if (machine == "arm64") { + return os + "-aarch64"; + } + return os + '-' + machine; } diff --git a/lib/maths/unittest/CBoostedTreeTest.cc b/lib/maths/unittest/CBoostedTreeTest.cc index bbbad12a09..f0e82934c8 100644 --- a/lib/maths/unittest/CBoostedTreeTest.cc +++ b/lib/maths/unittest/CBoostedTreeTest.cc @@ -1067,7 +1067,7 @@ BOOST_AUTO_TEST_CASE(testBinomialLogisticRegression) { LOG_DEBUG(<< "log relative error = " << maths::CBasicStatistics::mean(logRelativeError)); - BOOST_TEST_REQUIRE(maths::CBasicStatistics::mean(logRelativeError) < 0.65); + BOOST_TEST_REQUIRE(maths::CBasicStatistics::mean(logRelativeError) < 0.67); meanLogRelativeError.add(maths::CBasicStatistics::mean(logRelativeError)); } diff --git a/lib/maths/unittest/CLinearAlgebraTest.cc b/lib/maths/unittest/CLinearAlgebraTest.cc index 6199fdd2a3..a03860869a 100644 --- a/lib/maths/unittest/CLinearAlgebraTest.cc +++ b/lib/maths/unittest/CLinearAlgebraTest.cc @@ -227,7 +227,7 @@ BOOST_AUTO_TEST_CASE(testVectorNx1) { maths::CVectorNx1 sy = s * y; LOG_DEBUG(<< "Sy = " << sy); for (std::size_t i = 0u; i < 4; ++i) { - BOOST_REQUIRE_EQUAL(expected(i), sy(i)); + BOOST_REQUIRE_CLOSE_ABSOLUTE(expected(i), sy(i), 1e-14); } } } diff --git a/mk/macosx.mk b/mk/macosx.mk index 771cd79dd6..3a78d49df5 100644 --- a/mk/macosx.mk +++ b/mk/macosx.mk @@ -6,7 +6,8 @@ OS=MacOSX -CPP_PLATFORM_HOME=$(CPP_DISTRIBUTION_HOME)/platform/darwin-x86_64 +HARDWARE_ARCH:=$(shell uname -m | sed 's/arm64/aarch64/') +CPP_PLATFORM_HOME=$(CPP_DISTRIBUTION_HOME)/platform/darwin-$(HARDWARE_ARCH) ML_APP_NAME=controller APP_CONTENTS=$(ML_APP_NAME).app/Contents @@ -24,9 +25,13 @@ COVERAGE=--coverage endif endif +ifeq ($(HARDWARE_ARCH),x86_64) +ARCHCFLAGS=-msse4.2 +endif + SDK_PATH:=$(shell xcrun --show-sdk-path) # Start by enabling all warnings and then disable the really pointless/annoying ones -CFLAGS=-g $(OPTCFLAGS) -msse4.2 -fstack-protector -Weverything -Werror-switch -Wno-deprecated -Wno-disabled-macro-expansion -Wno-documentation-deprecated-sync -Wno-documentation-unknown-command -Wno-float-equal -Wno-gnu -Wno-missing-prototypes -Wno-padded -Wno-sign-conversion -Wno-unreachable-code -Wno-used-but-marked-unused $(COVERAGE) +CFLAGS=-g $(OPTCFLAGS) $(ARCHCFLAGS) -fstack-protector -Weverything -Werror-switch -Wno-deprecated -Wno-disabled-macro-expansion -Wno-documentation-deprecated-sync -Wno-documentation-unknown-command -Wno-float-equal -Wno-gnu -Wno-missing-prototypes -Wno-padded -Wno-poison-system-directories -Wno-sign-conversion -Wno-unreachable-code -Wno-used-but-marked-unused $(COVERAGE) CXXFLAGS=$(CFLAGS) -Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-exit-time-destructors -Wno-global-constructors -Wno-undefined-reinterpret-cast -Wno-unused-member-function -Wno-weak-vtables CPPFLAGS=-isystem $(CPP_SRC_HOME)/3rd_party/include -isystem /usr/local/include -D$(OS) $(OPTCPPFLAGS) ANALYZEFLAGS=--analyze @@ -46,21 +51,30 @@ RESOURCES_DIR=$(APP_CONTENTS)/Resources LOCALLIBS= NETLIBS= BOOSTVER=1_71 +ifeq ($(HARDWARE_ARCH),x86_64) +BOOSTARCH=x64 +else +BOOSTARCH=a64 +endif BOOSTCLANGVER:=$(shell $(CXX) --version | grep ' version ' | sed 's/.* version //' | awk -F. '{ print $$1$$2; }') # Use -isystem instead of -I for Boost headers to suppress warnings from Boost BOOSTINCLUDES=-isystem /usr/local/include/boost-$(BOOSTVER) BOOSTCPPFLAGS=-DBOOST_ALL_DYN_LINK -DBOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS -BOOSTLOGLIBS=-lboost_log-clang-darwin$(BOOSTCLANGVER)-mt-x64-$(BOOSTVER) -BOOSTLOGSETUPLIBS=-lboost_log_setup-clang-darwin$(BOOSTCLANGVER)-mt-x64-$(BOOSTVER) -BOOSTREGEXLIBS=-lboost_regex-clang-darwin$(BOOSTCLANGVER)-mt-x64-$(BOOSTVER) -BOOSTIOSTREAMSLIBS=-lboost_iostreams-clang-darwin$(BOOSTCLANGVER)-mt-x64-$(BOOSTVER) -BOOSTPROGRAMOPTIONSLIBS=-lboost_program_options-clang-darwin$(BOOSTCLANGVER)-mt-x64-$(BOOSTVER) -BOOSTTHREADLIBS=-lboost_thread-clang-darwin$(BOOSTCLANGVER)-mt-x64-$(BOOSTVER) -lboost_system-clang-darwin$(BOOSTCLANGVER)-mt-x64-$(BOOSTVER) -BOOSTFILESYSTEMLIBS=-lboost_filesystem-clang-darwin$(BOOSTCLANGVER)-mt-x64-$(BOOSTVER) -lboost_system-clang-darwin$(BOOSTCLANGVER)-mt-x64-$(BOOSTVER) -BOOSTDATETIMELIBS=-lboost_date_time-clang-darwin$(BOOSTCLANGVER)-mt-x64-$(BOOSTVER) -BOOSTTESTLIBS=-lboost_unit_test_framework-clang-darwin$(BOOSTCLANGVER)-mt-x64-$(BOOSTVER) +BOOSTLOGLIBS=-lboost_log-clang-darwin$(BOOSTCLANGVER)-mt-$(BOOSTARCH)-$(BOOSTVER) +BOOSTLOGSETUPLIBS=-lboost_log_setup-clang-darwin$(BOOSTCLANGVER)-mt-$(BOOSTARCH)-$(BOOSTVER) +BOOSTREGEXLIBS=-lboost_regex-clang-darwin$(BOOSTCLANGVER)-mt-$(BOOSTARCH)-$(BOOSTVER) +BOOSTIOSTREAMSLIBS=-lboost_iostreams-clang-darwin$(BOOSTCLANGVER)-mt-$(BOOSTARCH)-$(BOOSTVER) +BOOSTPROGRAMOPTIONSLIBS=-lboost_program_options-clang-darwin$(BOOSTCLANGVER)-mt-$(BOOSTARCH)-$(BOOSTVER) +BOOSTTHREADLIBS=-lboost_thread-clang-darwin$(BOOSTCLANGVER)-mt-$(BOOSTARCH)-$(BOOSTVER) -lboost_system-clang-darwin$(BOOSTCLANGVER)-mt-$(BOOSTARCH)-$(BOOSTVER) +BOOSTFILESYSTEMLIBS=-lboost_filesystem-clang-darwin$(BOOSTCLANGVER)-mt-$(BOOSTARCH)-$(BOOSTVER) -lboost_system-clang-darwin$(BOOSTCLANGVER)-mt-$(BOOSTARCH)-$(BOOSTVER) +BOOSTDATETIMELIBS=-lboost_date_time-clang-darwin$(BOOSTCLANGVER)-mt-$(BOOSTARCH)-$(BOOSTVER) +BOOSTTESTLIBS=-lboost_unit_test_framework-clang-darwin$(BOOSTCLANGVER)-mt-$(BOOSTARCH)-$(BOOSTVER) RAPIDJSONINCLUDES=-isystem $(CPP_SRC_HOME)/3rd_party/rapidjson/include +ifeq ($(HARDWARE_ARCH),x86_64) RAPIDJSONCPPFLAGS=-DRAPIDJSON_HAS_STDSTRING -DRAPIDJSON_SSE42 +else +RAPIDJSONCPPFLAGS=-DRAPIDJSON_HAS_STDSTRING -DRAPIDJSON_NEON +endif EIGENINCLUDES=-isystem $(CPP_SRC_HOME)/3rd_party/eigen EIGENCPPFLAGS=-DEIGEN_MPL2_ONLY -DEIGEN_MAX_ALIGN_BYTES=32 XMLINCLUDES=-isystem $(SDK_PATH)/usr/include/libxml2 diff --git a/set_env.sh b/set_env.sh index 2f13d54f17..0f85d30a1f 100755 --- a/set_env.sh +++ b/set_env.sh @@ -24,7 +24,7 @@ case `uname` in Darwin) SIMPLE_PLATFORM=macos - BUNDLE_PLATFORM=darwin-x86_64 + BUNDLE_PLATFORM=darwin-`uname -m | sed 's/arm64/aarch64/'` ;; Linux) @@ -91,10 +91,6 @@ case $SIMPLE_PLATFORM in PATH=/usr/local/gcc75/bin:/usr/bin:/bin:/usr/local/gcc75/sbin:/usr/sbin:/sbin:/usr/local/bin ;; - linux-musl) - PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin - ;; - macos) PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin ;; @@ -120,10 +116,6 @@ case $SIMPLE_PLATFORM in export LD_LIBRARY_PATH=/usr/local/gcc75/lib64:/usr/local/gcc75/lib:/usr/lib:/lib ;; - linux-musl) - export LD_LIBRARY_PATH=/usr/local/lib64:/usr/local/lib:/usr/lib:/lib - ;; - windows) # On Windows there's no separate library path - $PATH is used PATH=$CPP_PLATFORM_HOME/bin:$PATH:$ROOT/usr/local/lib