diff --git a/build-emacs-for-macos b/build-emacs-for-macos index 36d636b..de49204 100755 --- a/build-emacs-for-macos +++ b/build-emacs-for-macos @@ -1369,6 +1369,7 @@ class LibEmbedder < AbstractEmbedder debug "Copying '#{source}' to: '#{relative_path(target)}' ('#{dylib_id}')" FileUtils.mkdir_p(File.dirname(target)) cmd('cp', '-pRL', source, target) + fix_macho_sdk_version(target) next if dylib_id.nil? || dylib_id == '' @@ -1400,6 +1401,95 @@ class LibEmbedder < AbstractEmbedder end end + # This is a horrible hack attempt to fix a application signing verification + # bug with the x86_64 builds. It seems that most of the Intel based libraries + # from Nix uses `LC_VERSION_MIN_MACOSX`, most specify both `version` and `sdk` + # values. However some don't specify a `sdk` value, and when checked with + # `codeside -vv -d `, it complains with a warning: + # + # Library validation warning=OS X SDK version before 10.9 does not support Library Validation + # + # I suspect this is why the Intel builds fail signing verification. This is an + # attempt to fix that. It uses `vtool` to force set the newer + # `LC_BUILD_VERSION` load command's `minos` and `sdk` values to that of the + # old load command's `version` value. But only if the old load command is + # present and missing an `sdk` value. + def fix_macho_sdk_version(target_file) + mf = nil + + begin + mf = MachO.open(target_file) + rescue MachO::NotAMachOError => e + debug '-- Skipping Mach-O SDK version check/fix, not a Mach-O file' + return + end + + blank_legacy_sdk = false + legacy_version = nil + legacy_version_string = nil + lc_build_version = false + + mf.load_commands.each do |lc| + if lc.is_a?(MachO::LoadCommands::VersionMinCommand) && + lc.sdk.zero? && !lc.version.zero? + blank_legacy_sdk = true + legacy_version = lc.version + legacy_version_string = lc.version_string + end + + if lc.is_a?(MachO::LoadCommands::BuildVersionCommand) + lc_build_version = true + end + end + + return unless blank_legacy_sdk && !lc_build_version && legacy_version + + debug '-- Adding LC_BUILD_VERSION to Mach-O header' + run_cmd( + vtool_path, '-set-build-version', + 'macos', legacy_version_string, legacy_version_string, + '-replace', '-output', "#{target_file}.patched", target_file + ) + + while_writable(target_file) do + FileUtils.remove_file(target_file) + FileUtils.mv("#{target_file}.patched", target_file) + end + + begin + success = MachO.open(target_file).load_commands.any? do |lc| + lc.is_a?(MachO::LoadCommands::BuildVersionCommand) && + lc.minos == legacy_version && lc.sdk == legacy_version + end + + error "Failed to set LC_BUILD_VERSION in #{target_file}" unless success + rescue StandardError => e + debug '-- Failed to verify LC_BUILD_VERSION in Mach-O header' + raise e + end + end + + # Try and locate vtool. + def vtool_path + return @vtool_path if @vtool_path + + # Default approach + path = `xcrun -f vtool`.strip + return @vtool_path = path unless path.empty? + + # Unset DEVELOPER_DIR and try again. + path = `env DEVELOPER_DIR='' xcrun -f vtool`.strip + return @vtool_path = path unless path.empty? + + # Try with full path to xcrun. This should escape out of the nix environment + # and pull in vtool from macOS itself. + path = `env DEVELOPER_DIR='' /usr/bin/xcrun -f vtool`.strip + return @vtool_path = path unless path.empty? + + error 'Could not locate vtool, please ensure Xcode Command Line Tools ' \ + 'are installed' + end + def while_writable(file) mode = File.stat(file).mode File.chmod(0o775, file) diff --git a/flake.nix b/flake.nix index 9ad44cd..2025259 100644 --- a/flake.nix +++ b/flake.nix @@ -14,11 +14,12 @@ { devShells.default = pkgs.mkShell { packages = with pkgs; [ + apple-sdk_11 autoconf clang coreutils curl - apple-sdk + darwin.DarwinTools # sw_vers dbus expat findutils @@ -57,6 +58,8 @@ ]; shellHook = '' + export MACOSX_DEPLOYMENT_TARGET="11.0" + export DEVELOPER_DIR="${pkgs.apple-sdk_11}" export EMACS_BUILD_USE_NIX="true" export NIX_GCC_LIB_VERSION="${pkgs.gcc.cc.lib.version}" export NIX_GCC_LIB_ROOT="${pkgs.gcc.cc.lib.outPath}"