How to properly configure CMake script to run with osx_deployment_target 10.10?

History

I would like to build libgit2 project with the following script

#!/bin/sh

export MACOSX_DEPLOYMENT_TARGET=10.10

rm -rf "xcode"
mkdir "xcode"
cd "xcode"
cmake -G "Xcode" ..
open "libgit2.xcodeproj"

Open Xcode 12.5 and build git2 target.
Xcode complains about futimens function.
Warning is “‘futimens’ is only available on macOS 10.13 or newer”.

Issue at libgit2

Related issue at libgit2.

StackOverflow solution

I also tried this solution at StackOverflow.

I changed CMakeLists.txt at root directory by adding these lines.

IF (APPLE)
	OPTION(USE_ICONV		"Link with and use iconv library"			 ON)
	ADD_COMPILE_OPTIONS(-mmacosx-version-min=10.10) /// compile option
	set(CMAKE_OSX_DEPLOYMENT_TARGET "10.10" CACHE STRING "" FORCE) /// and cmake_osx_deployment target
ENDIF()

Nothing changed.

Actual

Xcode still complains about futimens function.
Could anybody help with proper configuration?

Environment

macOS 11.3.1
Xcode 12.5
Cmake 3.20.2
libgit2 ( latest main ).

CMAKE_OSX_DEPLOYMENT_TARGET (as a CMake variable) should do it. I wonder if check_function_exists needs some help here? Could you get the try_compile output from that bit with the --debug-trycompile flag?

Could you guide me about try_compile for my case?
I only added --debug-trycompile flag in my script.

cmake --debug-trycompile -G "Xcode" ..

The output is under CMakeFiles somewhere. You can put a message(FATAL_ERROR) after the futimens check to avoid extra work too.

-target is passed with value x86_64-apple-macos10.10.

Determining if the function futimens exists passed with the following output:
Change Dir: /Users/me/libgit2/xcode/CMakeFiles/CMakeTmp

Run Build Command(s):/usr/bin/xcodebuild -project CMAKE_TRY_COMPILE.xcodeproj build -target cmTC_50ec0 -parallelizeTargets -configuration Debug -hideShellScriptEnvironment && Command line invocation:
    /Applications/Xcode.app/Contents/Developer/usr/bin/xcodebuild -project CMAKE_TRY_COMPILE.xcodeproj build -target cmTC_50ec0 -parallelizeTargets -configuration Debug -hideShellScriptEnvironment

User defaults from command line:
    HideShellScriptEnvironment = YES
    IDEPackageSupportUseBuiltinSCM = YES

note: Using new build system
note: Building targets in parallel
note: Planning build
note: Analyzing workspace
note: Constructing build description
note: Build preparation complete

CompileC /Users/me/libgit2/xcode/CMakeFiles/CMakeTmp/CMAKE_TRY_COMPILE.build/Debug/cmTC_50ec0.build/Objects-normal/x86_64/CheckFunctionExists.o /usr/local/Cellar/cmake/3.20.2/share/cmake/Modules/CheckFunctionExists.c normal x86_64 c com.apple.compilers.llvm.clang.1_0.compiler (in target 'cmTC_50ec0' from project 'CMAKE_TRY_COMPILE')
    cd /Users/me/libgit2/xcode/CMakeFiles/CMakeTmp
    /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -x c 
    -target x86_64-apple-macos10.10 
    -fmessage-length\=0 
    -fdiagnostics-show-note-include-stack 
    -fmacro-backtrace-limit\=0 -Wno-trigraphs 
    -fpascal-strings 
    -O0 
    -Wno-missing-field-initializers 
    -Wno-missing-prototypes
    -Wno-return-type
    -Wno-missing-braces
    -Wparentheses
    -Wswitch
    -Wno-unused-function
    -Wno-unused-label
    -Wno-unused-parameter
    -Wno-unused-variable
    -Wunused-value
    -Wno-empty-body
    -Wno-uninitialized
    -Wno-unknown-pragmas
    -Wno-shadow
    -Wno-four-char-constants
    -Wno-conversion
    -Wno-constant-conversion
    -Wno-int-conversion
    -Wno-bool-conversion
    -Wno-enum-conversion
    -Wno-float-conversion
    -Wno-non-literal-null-conversion
    -Wno-objc-literal-conversion
    -Wno-shorten-64-to-32
    -Wpointer-sign
    -Wno-newline-eof
    -DCMAKE_INTDIR\=\"Debug\"
    -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.3.sdk
    -fasm-blocks
    -fstrict-aliasing
    -Wdeprecated-declarations
    -g
    -Wno-sign-conversion
    -Wno-infinite-recursion
    -Wno-comma
    -Wno-block-capture-autoreleasing
    -Wno-strict-prototypes
    -Wno-semicolon-before-method-body
    -I/Users/me/libgit2/xcode/CMakeFiles/CMakeTmp/Debug/include
    -I/Users/me/libgit2/xcode/CMakeFiles/CMakeTmp/CMAKE_TRY_COMPILE.build/Debug/cmTC_50ec0.build/DerivedSources-normal/x86_64
    -I/Users/me/libgit2/xcode/CMakeFiles/CMakeTmp/CMAKE_TRY_COMPILE.build/Debug/cmTC_50ec0.build/DerivedSources/x86_64
    -I/Users/me/libgit2/xcode/CMakeFiles/CMakeTmp/CMAKE_TRY_COMPILE.build/Debug/cmTC_50ec0.build/DerivedSources
    -F/Users/me/libgit2/xcode/CMakeFiles/CMakeTmp/Debug
    -D_GNU_SOURCE
    -Wall
    -Wextra
    -fvisibility\=hidden
    -fPIC
    -Wdocumentation
    -Wno-documentation-deprecated-sync
    -Wno-missing-field-initializers
    -Wstrict-aliasing
    -Wstrict-prototypes
    -Wdeclaration-after-statement
    -Wshift-count-overflow
    -Wunused-const-variable
    -Wunused-function
    -Wint-conversion
    -Wformat
    -Wformat-security
    -Wmissing-declarations
    -DCHECK_FUNCTION_EXISTS\=futimens
    -MMD
    -MT dependencies
    -MF /Users/me/libgit2/xcode/CMakeFiles/CMakeTmp/CMAKE_TRY_COMPILE.build/Debug/cmTC_50ec0.build/Objects-normal/x86_64/CheckFunctionExists.d
    --serialize-diagnostics /Users/me/libgit2/xcode/CMakeFiles/CMakeTmp/CMAKE_TRY_COMPILE.build/Debug/cmTC_50ec0.build/Objects-normal/x86_64/CheckFunctionExists.dia
    -c /usr/local/Cellar/cmake/3.20.2/share/cmake/Modules/CheckFunctionExists.c
    -o /Users/me/libgit2/xcode/CMakeFiles/CMakeTmp/CMAKE_TRY_COMPILE.build/Debug/cmTC_50ec0.build/Objects-normal/x86_64/CheckFunctionExists.o

WriteAuxiliaryFile /Users/me/libgit2/xcode/CMakeFiles/CMakeTmp/CMAKE_TRY_COMPILE.build/Debug/cmTC_50ec0.build/Objects-normal/x86_64/cmTC_50ec0.LinkFileList (in target 'cmTC_50ec0' from project 'CMAKE_TRY_COMPILE')
    cd /Users/me/libgit2/xcode/CMakeFiles/CMakeTmp
    write-file /Users/me/libgit2/xcode/CMakeFiles/CMakeTmp/CMAKE_TRY_COMPILE.build/Debug/cmTC_50ec0.build/Objects-normal/x86_64/cmTC_50ec0.LinkFileList

Ld /Users/me/libgit2/xcode/CMakeFiles/CMakeTmp/Debug/cmTC_50ec0 normal (in target 'cmTC_50ec0' from project 'CMAKE_TRY_COMPILE')
    cd /Users/me/libgit2/xcode/CMakeFiles/CMakeTmp
    /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang
    -target x86_64-apple-macos10.10
    -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.3.sdk
    -L/Users/me/libgit2/xcode/CMakeFiles/CMakeTmp/Debug
    -F/Users/me/libgit2/xcode/CMakeFiles/CMakeTmp/Debug
    -filelist /Users/me/libgit2/xcode/CMakeFiles/CMakeTmp/CMAKE_TRY_COMPILE.build/Debug/cmTC_50ec0.build/Objects-normal/x86_64/cmTC_50ec0.LinkFileList
    -Xlinker -object_path_lto 
    -Xlinker /Users/me/libgit2/xcode/CMakeFiles/CMakeTmp/CMAKE_TRY_COMPILE.build/Debug/cmTC_50ec0.build/Objects-normal/x86_64/cmTC_50ec0_lto.o
    -Xlinker -no_deduplicate 
    -Wl,-search_paths_first 
    -Wl,-headerpad_max_install_names 
    -Xlinker -dependency_info 
    -Xlinker /Users/me/libgit2/xcode/CMakeFiles/CMakeTmp/CMAKE_TRY_COMPILE.build/Debug/cmTC_50ec0.build/Objects-normal/x86_64/cmTC_50ec0_dependency_info.dat
    -o /Users/me/libgit2/xcode/CMakeFiles/CMakeTmp/Debug/cmTC_50ec0

RegisterExecutionPolicyException /Users/me/libgit2/xcode/CMakeFiles/CMakeTmp/Debug/cmTC_50ec0 (in target 'cmTC_50ec0' from project 'CMAKE_TRY_COMPILE')
    cd /Users/me/libgit2/xcode/CMakeFiles/CMakeTmp
    builtin-RegisterExecutionPolicyException /Users/me/libgit2/xcode/CMakeFiles/CMakeTmp/Debug/cmTC_50ec0

** BUILD SUCCEEDED **

Hmm. So why did the build succeed? Maybe it should be tested for existence under -Werror=deprecation (err, not that one, but whatever flag makes sense)?

I guess that I find a problem.
It seems that the problem is out of scope. I tried to compile a representative file at related CMake issue and it fails.

$ cat check.c
#include <fcntl.h>
#include <sys/stat.h>
int main()
{
  utimensat(0,0,0,0);
  return 0;
}
$ cc check.c -isysroot /Applications/Xcode-9-beta.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.13.sdk -mmacosx-version-min=10.11
check.c:5:3: warning: 'utimensat' is only available on macOS 10.13 or newer [-Wunguarded-availability-new]
  utimensat(0,0,0,0);
  ^~~~~~~~~

In my case it compiles without a hassle with all warnings enabled. I open a xcodeproject file and I find that Xcode doesn’t include <sys/stat.h> header. So, my function is just defined as futimens. ( char futimens(void) - template ).

Could I somehow add or pass as parameters these headers to check_function_exists?

Current Version

If I use check_function_exists, it doesn’t include any related header to search for a function. Maybe it is a setting in these CMakeLIsts.txt

enable_warnings(error)
# check_symbol_exists(futimens sys/stat.h HAVE_FUTIMENS)
check_function_exists(futimens HAVE_FUTIMENS)
IF (HAVE_FUTIMENS)
	SET(GIT_USE_FUTIMENS 1)
ENDIF()
message(FATAL_ERROR)

Source file is

char futimens(void);
int main(int ac, char* av[])
{
  CHECK_FUNCTION_EXISTS();
  if (ac > 1000) {
    return *av[0];
  }
  return 0;
}

Proposed solution

My solution is check_symbol_exists.

enable_warnings(error)
check_symbol_exists(futimens sys/stat.h HAVE_FUTIMENS)
# check_function_exists(futimens HAVE_FUTIMENS)
IF (HAVE_FUTIMENS)
	SET(GIT_USE_FUTIMENS 1)
ENDIF()
message(FATAL_ERROR)

And related source file is

/* */
#include <sys/stat.h>

int main(int argc, char** argv)
{
  (void)argv;
#ifndef futimens
  return ((int*)(&futimens))[argc]; // error
#else
  (void)argc;
  return 0;
#endif
}

I guess that it should be done in different way, no? check_function_exists should work, right?

There are differences between whether the symbol is a macro or an actual function I think. I would think that using check_symbol_exists is the way to fix this.

But CMake issue is closed.
It should work, am I right?

I guess that I do something wrong or this script is broken.

Besides, if I understand it correctly, CMake do a pretty easy test for errors by compiling a program with given function invocation. In check_function_exists this macros will be substituted by provided value futimens.

But, what I really don’t understand, why example program doesn’t contain headers with “possible” symbol of a function. They should include it, no? How will compiler find a header with this function?

Which issue?

Yes, if the function being tested is in sys/stat.h, the example program will need to include that. This really looks like an issue in the libgit2 CMake code to me.

CMake Issue.

CMake has a note at check_function_exists help page.


Prefer using CheckSymbolExists instead of this module, for the following reasons:

  • check_function_exists() can’t detect functions that are inlined in headers or specified as a macro.
  • check_function_exists() can’t detect anything in the 32-bit versions of the Win32 API, because of a mismatch in calling conventions.
  • check_function_exists() only verifies linking, it does not verify that the function is declared in system headers.

futimens neither inlined nor a macros nor a part of Win32 API.
What I’m worried about is a last point.
What if a function isn’t declared but it exists in previous versions of macOS?
It makes sense why check_function_exists confirms existence of futimens.