summaryrefslogtreecommitdiffstats
path: root/Tests
diff options
context:
space:
mode:
authorChris Mahoney <chris.mahoney@kitware.com>2023-06-05 19:59:13 (GMT)
committerBrad King <brad.king@kitware.com>2023-07-19 14:45:33 (GMT)
commit95941fd99020bb2345869ed393abde1a44529837 (patch)
tree4e809b9c121ce0e5745c1030d6a7c62b1c09bf2e /Tests
parentaf9489a4f2858db0bf666c29f99dd332426f5ca1 (diff)
downloadCMake-95941fd99020bb2345869ed393abde1a44529837.zip
CMake-95941fd99020bb2345869ed393abde1a44529837.tar.gz
CMake-95941fd99020bb2345869ed393abde1a44529837.tar.bz2
add_custom_{target,command}: Add argument JOB_SERVER_AWARE
Issue: #16273
Diffstat (limited to 'Tests')
-rw-r--r--Tests/RunCMake/CMakeLists.txt3
-rw-r--r--Tests/RunCMake/Make/DetectJobServer-absent-parallel-build-stderr.txt1
-rw-r--r--Tests/RunCMake/Make/DetectJobServer-absent.cmake13
-rw-r--r--Tests/RunCMake/Make/DetectJobServer-present.cmake13
-rw-r--r--Tests/RunCMake/Make/GNUMakeJobServerAware-check.cmake12
-rw-r--r--Tests/RunCMake/Make/GNUMakeJobServerAware.cmake12
-rw-r--r--Tests/RunCMake/Make/RunCMakeTest.cmake40
-rw-r--r--Tests/RunCMake/detect_jobserver.c204
8 files changed, 297 insertions, 1 deletions
diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt
index 8baa98f..584edd7 100644
--- a/Tests/RunCMake/CMakeLists.txt
+++ b/Tests/RunCMake/CMakeLists.txt
@@ -171,8 +171,9 @@ endif()
if(NOT CMAKE_GENERATOR MATCHES "Visual Studio|Xcode")
add_RunCMake_test(CMP0065 -DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME})
endif()
+add_executable(detect_jobserver detect_jobserver.c)
if(CMAKE_GENERATOR MATCHES "Make")
- add_RunCMake_test(Make -DMAKE_IS_GNU=${MAKE_IS_GNU})
+ add_RunCMake_test(Make -DMAKE_IS_GNU=${MAKE_IS_GNU} -DDETECT_JOBSERVER=$<TARGET_FILE:detect_jobserver>)
endif()
unset(ninja_test_with_qt_version)
unset(ninja_qt_args)
diff --git a/Tests/RunCMake/Make/DetectJobServer-absent-parallel-build-stderr.txt b/Tests/RunCMake/Make/DetectJobServer-absent-parallel-build-stderr.txt
new file mode 100644
index 0000000..c63bde3
--- /dev/null
+++ b/Tests/RunCMake/Make/DetectJobServer-absent-parallel-build-stderr.txt
@@ -0,0 +1 @@
+^(Warning: (Borland's make|NMake|Watcom's WMake) does not support parallel builds\. Ignoring parallel build command line option\.)?$
diff --git a/Tests/RunCMake/Make/DetectJobServer-absent.cmake b/Tests/RunCMake/Make/DetectJobServer-absent.cmake
new file mode 100644
index 0000000..e3dddc0
--- /dev/null
+++ b/Tests/RunCMake/Make/DetectJobServer-absent.cmake
@@ -0,0 +1,13 @@
+# Verifies that the jobserver connection is absent
+add_custom_command(OUTPUT custom_command.txt
+ JOB_SERVER_AWARE OFF
+ COMMENT "Should not detect jobserver"
+ COMMAND ${DETECT_JOBSERVER} --absent "custom_command.txt"
+)
+
+# trigger the custom command to run
+add_custom_target(dummy ALL
+ JOB_SERVER_AWARE OFF
+ DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/custom_command.txt
+ COMMAND ${DETECT_JOBSERVER} --absent "custom_target.txt"
+)
diff --git a/Tests/RunCMake/Make/DetectJobServer-present.cmake b/Tests/RunCMake/Make/DetectJobServer-present.cmake
new file mode 100644
index 0000000..a33658f
--- /dev/null
+++ b/Tests/RunCMake/Make/DetectJobServer-present.cmake
@@ -0,0 +1,13 @@
+# Verifies that the jobserver is present
+add_custom_command(OUTPUT custom_command.txt
+ JOB_SERVER_AWARE ON
+ COMMENT "Should detect jobserver support"
+ COMMAND ${DETECT_JOBSERVER} --present "custom_command.txt"
+)
+
+# trigger the custom command to run
+add_custom_target(dummy ALL
+ JOB_SERVER_AWARE ON
+ DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/custom_command.txt
+ COMMAND ${DETECT_JOBSERVER} --present "custom_target.txt"
+)
diff --git a/Tests/RunCMake/Make/GNUMakeJobServerAware-check.cmake b/Tests/RunCMake/Make/GNUMakeJobServerAware-check.cmake
new file mode 100644
index 0000000..7c5c296
--- /dev/null
+++ b/Tests/RunCMake/Make/GNUMakeJobServerAware-check.cmake
@@ -0,0 +1,12 @@
+# This test verifies that the commands in the generated Makefiles contain the
+# `+` prefix
+function(check_for_plus_prefix target)
+ set(file "${RunCMake_BINARY_DIR}/GNUMakeJobServerAware-build/${target}")
+ file(READ "${file}" build_file)
+ if(NOT "${build_file}" MATCHES [[\+]])
+ message(FATAL_ERROR "The file ${file} does not contain the expected prefix in the custom command.")
+ endif()
+endfunction()
+
+check_for_plus_prefix("CMakeFiles/dummy.dir/build.make")
+check_for_plus_prefix("CMakeFiles/dummy2.dir/build.make")
diff --git a/Tests/RunCMake/Make/GNUMakeJobServerAware.cmake b/Tests/RunCMake/Make/GNUMakeJobServerAware.cmake
new file mode 100644
index 0000000..951c2d7
--- /dev/null
+++ b/Tests/RunCMake/Make/GNUMakeJobServerAware.cmake
@@ -0,0 +1,12 @@
+add_custom_command(
+ OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/custom-command"
+ JOB_SERVER_AWARE ON
+ COMMAND $(CMAKE_COMMAND) -E touch "${CMAKE_CURRENT_BINARY_DIR}/custom-command"
+)
+add_custom_target(dummy ALL DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/custom-command")
+
+add_custom_target(
+ dummy2 ALL
+ JOB_SERVER_AWARE ON
+ COMMAND ${CMAKE_COMMAND} -E true
+)
diff --git a/Tests/RunCMake/Make/RunCMakeTest.cmake b/Tests/RunCMake/Make/RunCMakeTest.cmake
index c7717ec..12904c8 100644
--- a/Tests/RunCMake/Make/RunCMakeTest.cmake
+++ b/Tests/RunCMake/Make/RunCMakeTest.cmake
@@ -70,3 +70,43 @@ if(NOT RunCMake_GENERATOR STREQUAL "Watcom WMake")
run_CMP0113(OLD)
run_CMP0113(NEW)
endif()
+
+function(detect_jobserver_present is_parallel)
+ set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/DetectJobServer-present-build)
+ set(RunCMake_TEST_NO_CLEAN 1)
+ set(RunCMake_TEST_OPTIONS "-DDETECT_JOBSERVER=${DETECT_JOBSERVER}")
+ run_cmake(DetectJobServer-present)
+ if (is_parallel)
+ run_cmake_command(DetectJobServer-present-parallel-build ${CMAKE_COMMAND} --build . -j4)
+ else()
+ run_cmake_command(DetectJobServer-present-build ${CMAKE_COMMAND} --build .)
+ endif()
+endfunction()
+
+function(detect_jobserver_absent is_parallel)
+ set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/DetectJobServer-absent-build)
+ set(RunCMake_TEST_NO_CLEAN 1)
+ set(RunCMake_TEST_OPTIONS "-DDETECT_JOBSERVER=${DETECT_JOBSERVER}")
+ run_cmake(DetectJobServer-absent)
+ if (is_parallel)
+ run_cmake_command(DetectJobServer-absent-parallel-build ${CMAKE_COMMAND} --build . -j4)
+ else()
+ run_cmake_command(DetectJobServer-absent-build ${CMAKE_COMMAND} --build .)
+ endif()
+endfunction()
+
+# Jobservers are currently only supported by GNU makes, except MSYS2 make
+if(MAKE_IS_GNU AND NOT RunCMake_GENERATOR MATCHES "MSYS Makefiles")
+ detect_jobserver_present(ON)
+else()
+ detect_jobserver_absent(ON)
+endif()
+# No matter which generator is used, the jobserver should not be present if a
+# parallel build is not requested
+detect_jobserver_absent(OFF)
+
+if(MAKE_IS_GNU)
+ # In GNU makes, `JOB_SERVER_AWARE` support is implemented by prefixing
+ # commands with the '+' operator.
+ run_cmake(GNUMakeJobServerAware)
+endif()
diff --git a/Tests/RunCMake/detect_jobserver.c b/Tests/RunCMake/detect_jobserver.c
new file mode 100644
index 0000000..a6c1a7c
--- /dev/null
+++ b/Tests/RunCMake/detect_jobserver.c
@@ -0,0 +1,204 @@
+#ifndef _CRT_SECURE_NO_WARNINGS
+# define _CRT_SECURE_NO_WARNINGS
+#endif
+
+#if defined(_MSC_VER) && _MSC_VER >= 1928
+# pragma warning(disable : 5105) /* macro expansion warning in windows.h */
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define MAX_MESSAGE_LENGTH 1023
+#define USAGE "Usage: %s [--present|--absent] <output_file>\n"
+
+// Extracts --jobserver-auth=<string> or --jobserver-fds=<string> from
+// MAKEFLAGS. The returned pointer points to the start of <string> Returns NULL
+// if MAKEFLAGS is not set or does not contain --jobserver-auth or
+// --jobserver-fds
+char* jobserver_auth(char* message)
+{
+ const char* jobserver_auth = "--jobserver-auth=";
+ const char* jobserver_fds = "--jobserver-fds=";
+ char* auth;
+ char* fds;
+ char* start;
+ char* end;
+ char* result;
+ size_t len;
+
+ char* makeflags = getenv("MAKEFLAGS");
+ if (makeflags == NULL) {
+ strncpy(message, "MAKEFLAGS not set", MAX_MESSAGE_LENGTH);
+ return NULL;
+ }
+
+ // write MAKEFLAGS to stdout for debugging
+ fprintf(stdout, "MAKEFLAGS: %s\n", makeflags);
+
+ auth = strstr(makeflags, jobserver_auth);
+ fds = strstr(makeflags, jobserver_fds);
+ if (auth == NULL && fds == NULL) {
+ strncpy(message, "No jobserver found", MAX_MESSAGE_LENGTH);
+ return NULL;
+ } else if (auth != NULL) {
+ start = auth + strlen(jobserver_auth);
+ } else {
+ start = fds + strlen(jobserver_fds);
+ }
+
+ end = strchr(start, ' ');
+ if (end == NULL) {
+ end = start + strlen(start);
+ }
+ len = (size_t)(end - start);
+ result = (char*)malloc(len + 1);
+ strncpy(result, start, len);
+ result[len] = '\0';
+
+ return result;
+}
+
+#if defined(_WIN32)
+# include <windows.h>
+
+int windows_semaphore(const char* semaphore, char* message)
+{
+ // Open the semaphore
+ HANDLE hSemaphore = OpenSemaphoreA(SEMAPHORE_ALL_ACCESS, FALSE, semaphore);
+
+ if (hSemaphore == NULL) {
+# if defined(_MSC_VER) && _MSC_VER < 1900
+ sprintf(message, "Error opening semaphore: %s (%ld)\n", semaphore,
+ GetLastError());
+# else
+ snprintf(message, MAX_MESSAGE_LENGTH,
+ "Error opening semaphore: %s (%ld)\n", semaphore, GetLastError());
+# endif
+ return 1;
+ }
+
+ strncpy(message, "Success", MAX_MESSAGE_LENGTH);
+ return 0;
+}
+#else
+# include <errno.h>
+# include <fcntl.h>
+
+int test_fd(int read_fd, int write_fd, char* message)
+{
+ // Detect if the file descriptors are valid
+ int read_good = fcntl(read_fd, F_GETFD) != -1;
+ int read_error = errno;
+
+ int write_good = fcntl(write_fd, F_GETFD) != -1;
+ int write_error = errno;
+
+ if (!read_good || !write_good) {
+ snprintf(message, MAX_MESSAGE_LENGTH,
+ "Error opening file descriptors: %d (%s), %d (%s)\n", read_fd,
+ strerror(read_error), write_fd, strerror(write_error));
+ return 1;
+ }
+
+ snprintf(message, MAX_MESSAGE_LENGTH, "Success\n");
+ return 0;
+}
+
+int posix(const char* jobserver, char* message)
+{
+ int read_fd;
+ int write_fd;
+ const char* path;
+
+ // First try to parse as "R,W" file descriptors
+ if (sscanf(jobserver, "%d,%d", &read_fd, &write_fd) == 2) {
+ return test_fd(read_fd, write_fd, message);
+ }
+
+ // Then try to parse as "fifo:PATH"
+ if (strncmp(jobserver, "fifo:", 5) == 0) {
+ path = jobserver + 5;
+ read_fd = open(path, O_RDONLY);
+ write_fd = open(path, O_WRONLY);
+ return test_fd(read_fd, write_fd, message);
+ }
+
+ // We don't understand the format
+ snprintf(message, MAX_MESSAGE_LENGTH, "Unrecognized jobserver format: %s\n",
+ jobserver);
+ return 1;
+}
+#endif
+
+// Takes 2 arguments:
+// Either --present or --absent to indicate we expect the jobserver to be
+// "present and valid", or "absent or invalid"
+//
+// if `--present` is passed, the exit code will be 0 if the jobserver is
+// present, 1 if it is absent if `--absent` is passed, the exit code will be 0
+// if the jobserver is absent, 1 if it is present in either case, if there is
+// some fatal error (e.g the output file cannot be opened), the exit code will
+// be 2
+int main(int argc, char** argv)
+{
+ char message[MAX_MESSAGE_LENGTH + 1];
+ char* output_file;
+ FILE* fp;
+ int expecting_present;
+ int expecting_absent;
+ char* jobserver;
+ int result;
+
+ if (argc != 3) {
+ fprintf(stderr, USAGE, argv[0]);
+ return 2;
+ }
+
+ expecting_present = strcmp(argv[1], "--present") == 0;
+ expecting_absent = strcmp(argv[1], "--absent") == 0;
+ if (!expecting_present && !expecting_absent) {
+ fprintf(stderr, USAGE, argv[0]);
+ return 2;
+ }
+
+ output_file = argv[2];
+ fp = fopen(output_file, "w");
+ if (fp == NULL) {
+ fprintf(stderr, "Error opening output file: %s\n", output_file);
+ return 2;
+ }
+
+ jobserver = jobserver_auth(message);
+ if (jobserver == NULL) {
+ if (expecting_absent) {
+ fprintf(stdout, "Success\n");
+ return 0;
+ }
+
+ fprintf(stderr, "%s\n", message);
+ return 1;
+ }
+
+#if defined(_WIN32)
+ result = windows_semaphore(jobserver, message);
+#else
+ result = posix(jobserver, message);
+#endif
+ free(jobserver);
+ message[MAX_MESSAGE_LENGTH] = 0;
+
+ if (result == 0 && expecting_present) {
+ fprintf(stdout, "Success\n");
+ return 0;
+ }
+
+ if (result == 1 && expecting_absent) {
+ fprintf(stdout, "Success\n");
+ return 0;
+ }
+
+ fprintf(stderr, "%s\n", message);
+ return 1;
+}