diff options
author | Chris Mahoney <chris.mahoney@kitware.com> | 2023-06-05 19:59:13 (GMT) |
---|---|---|
committer | Brad King <brad.king@kitware.com> | 2023-07-19 14:45:33 (GMT) |
commit | 95941fd99020bb2345869ed393abde1a44529837 (patch) | |
tree | 4e809b9c121ce0e5745c1030d6a7c62b1c09bf2e /Tests | |
parent | af9489a4f2858db0bf666c29f99dd332426f5ca1 (diff) | |
download | CMake-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.txt | 3 | ||||
-rw-r--r-- | Tests/RunCMake/Make/DetectJobServer-absent-parallel-build-stderr.txt | 1 | ||||
-rw-r--r-- | Tests/RunCMake/Make/DetectJobServer-absent.cmake | 13 | ||||
-rw-r--r-- | Tests/RunCMake/Make/DetectJobServer-present.cmake | 13 | ||||
-rw-r--r-- | Tests/RunCMake/Make/GNUMakeJobServerAware-check.cmake | 12 | ||||
-rw-r--r-- | Tests/RunCMake/Make/GNUMakeJobServerAware.cmake | 12 | ||||
-rw-r--r-- | Tests/RunCMake/Make/RunCMakeTest.cmake | 40 | ||||
-rw-r--r-- | Tests/RunCMake/detect_jobserver.c | 204 |
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; +} |