Add -E cmake_llvm_rc to preprocess files for llvm-rc

llvm-rc requires preprocessed rc files. The CMake command line tool
cmake_llvm_rc enables channing the preprocessor call and the resource
compiler and make this appear as single compilation step.

When llvm-rc is detected as resource compiler, the RC compilation step
is set to use this command.
This commit is contained in:
Thomas Bernard 2020-01-14 11:31:34 +01:00 committed by Brad King
parent 5f04dfe57e
commit 1c2d031cbd
12 changed files with 169 additions and 0 deletions

@ -133,6 +133,23 @@ if("x${CMAKE_C_SIMULATE_ID}" STREQUAL "xMSVC"
if ( "x${CMAKE_CXX_COMPILER_FRONTEND_VARIANT}" STREQUAL "xMSVC" OR "x${CMAKE_C_COMPILER_FRONTEND_VARIANT}" STREQUAL "xMSVC" )
include(Platform/Windows-MSVC)
# Feed the preprocessed rc file to llvm-rc
if(CMAKE_RC_COMPILER_INIT STREQUAL "llvm-rc")
if(DEFINED CMAKE_C_COMPILER)
set(CMAKE_RC_PREPROCESSOR CMAKE_C_COMPILER)
elseif(DEFINED CMAKE_CXX_COMPILER)
set(CMAKE_RC_PREPROCESSOR CMAKE_CXX_COMPILER)
endif()
if(DEFINED CMAKE_RC_PREPROCESSOR)
set(CMAKE_RC_COMPILE_OBJECT "${CMAKE_COMMAND} -E cmake_llvm_rc <OBJECT>.pp <${CMAKE_RC_PREPROCESSOR}> <DEFINES> -DRC_INVOKED <INCLUDES> <FLAGS> -clang:-MD -clang:-MF -clang:<SOURCE>.d -E <SOURCE> -- <CMAKE_RC_COMPILER> <FLAGS> /fo <OBJECT> <OBJECT>.pp")
if(CMAKE_GENERATOR STREQUAL "Ninja")
set(CMAKE_NINJA_CMCLDEPS_RC 0)
set(CMAKE_NINJA_DEP_TYPE_RC gcc)
endif()
unset(CMAKE_RC_PREPROCESSOR)
endif()
endif()
macro(__windows_compiler_clang lang)
set(_COMPILE_${lang} "${_COMPILE_${lang}_MSVC}")
__windows_compiler_msvc(${lang})

@ -4,6 +4,10 @@
#include <cmext/algorithm>
#include <fcntl.h>
#include "cm_uv.h"
#include "cmAlgorithms.h"
#include "cmDuration.h"
#include "cmGlobalGenerator.h"
@ -17,6 +21,7 @@
#include "cmStateSnapshot.h"
#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"
#include "cmUVProcessChain.h"
#include "cmUtils.hxx"
#include "cmVersion.h"
#include "cmake.h"
@ -1129,6 +1134,10 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args)
return cmcmd::VisualStudioLink(args, 2);
}
if (args[1] == "cmake_llvm_rc") {
return cmcmd::RunLLVMRC(args);
}
// Internal CMake color makefile support.
if (args[1] == "cmake_echo_color") {
return cmcmd::ExecuteEchoColor(args);
@ -1660,6 +1669,108 @@ int cmcmd::WindowsCEEnvironment(const char* version, const std::string& name)
return -1;
}
int cmcmd::RunPreprocessor(const std::vector<std::string>& command,
const std::string& intermediate_file)
{
cmUVProcessChainBuilder builder;
uv_fs_t fs_req;
int preprocessedFile =
uv_fs_open(nullptr, &fs_req, intermediate_file.c_str(), O_CREAT | O_RDWR,
0644, nullptr);
uv_fs_req_cleanup(&fs_req);
builder
.SetExternalStream(cmUVProcessChainBuilder::Stream_OUTPUT,
preprocessedFile)
.SetBuiltinStream(cmUVProcessChainBuilder::Stream_ERROR)
.AddCommand(command);
auto process = builder.Start();
if (!process.Valid()) {
std::cerr << "Failed to start preprocessor.";
return 1;
}
if (!process.Wait()) {
std::cerr << "Failed to wait for preprocessor";
return 1;
}
auto status = process.GetStatus();
if (!status[0] || status[0]->ExitStatus != 0) {
return 1;
}
return 0;
}
int cmcmd::RunLLVMRC(std::vector<std::string> const& args)
{
// The arguments are
// args[0] == <cmake-executable>
// args[1] == cmake_llvm_rc
// args[2] == intermediate_file
// args[3..n] == preprocess+args
// args[n+1] == --
// args[n+2...] == llvm-rc+args
if (args.size() < 3) {
std::cerr << "Invalid cmake_llvm_rc arguments";
return 1;
}
const std::string& intermediate_file = args[2];
std::vector<std::string> preprocess;
std::vector<std::string> resource_compile;
std::vector<std::string>* pArgTgt = &preprocess;
for (std::string const& arg : cmMakeRange(args).advance(3)) {
if (arg == "--") {
pArgTgt = &resource_compile;
} else {
pArgTgt->push_back(arg);
}
}
if (preprocess.empty()) {
std::cerr << "Empty preprocessing command";
return 1;
}
if (resource_compile.empty()) {
std::cerr << "Empty resource compilation command";
return 1;
}
auto result = RunPreprocessor(preprocess, intermediate_file);
if (result != 0) {
cmSystemTools::RemoveFile(intermediate_file);
return result;
}
cmUVProcessChainBuilder builder;
builder.SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT)
.SetBuiltinStream(cmUVProcessChainBuilder::Stream_ERROR)
.AddCommand(resource_compile);
auto process = builder.Start();
result = 0;
if (!process.Valid()) {
std::cerr << "Failed to start resource compiler.";
result = 1;
} else {
if (!process.Wait()) {
std::cerr << "Failed to wait for resource compiler";
result = 1;
}
}
cmSystemTools::RemoveFile(intermediate_file);
if (result != 0) {
return result;
}
auto status = process.GetStatus();
if (!status[0] || status[0]->ExitStatus != 0) {
return 1;
}
return 0;
}
class cmVSLink
{
int Type;

@ -31,6 +31,9 @@ protected:
static int ExecuteLinkScript(std::vector<std::string> const& args);
static int WindowsCEEnvironment(const char* version,
const std::string& name);
static int RunPreprocessor(const std::vector<std::string>& command,
const std::string& intermediate_file);
static int RunLLVMRC(std::vector<std::string> const& args);
static int VisualStudioLink(std::vector<std::string> const& args, int type);
};

@ -667,3 +667,33 @@ if(CMAKE_HOST_UNIX AND NOT CMAKE_SYSTEM_NAME STREQUAL "CYGWIN")
run_cmake_command(closed_stderr sh -c "\"${CMAKE_COMMAND}\" --version 2>&-")
run_cmake_command(closed_stdall sh -c "\"${CMAKE_COMMAND}\" --version <&- >&- 2>&-")
endif()
function(run_llvm_rc)
set(RunCMake_TEST_BINARY_DIR "${RunCMake_BINARY_DIR}/llvm_rc-build")
set(RunCMake_TEST_NO_CLEAN 1)
file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}")
file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}")
run_cmake_command(llvm_rc_no_args ${CMAKE_COMMAND} -E cmake_llvm_rc)
run_cmake_command(llvm_rc_no_-- ${CMAKE_COMMAND} -E cmake_llvm_rc test.tmp ${CMAKE_COMMAND} -E echo "This is a test")
run_cmake_command(llvm_rc_empty_preprocessor ${CMAKE_COMMAND} -E cmake_llvm_rc test.tmp -- ${CMAKE_COMMAND} -E echo "This is a test")
run_cmake_command(llvm_rc_failing_first_command ${CMAKE_COMMAND} -E cmake_llvm_rc test.tmp ${CMAKE_COMMAND} -E false -- ${CMAKE_COMMAND} -E echo "This is a test")
run_cmake_command(llvm_rc_failing_second_command ${CMAKE_COMMAND} -E cmake_llvm_rc test.tmp ${CMAKE_COMMAND} -E echo "This is a test" -- ${CMAKE_COMMAND} -E false )
if(EXISTS ${RunCMake_TEST_BINARY_DIR}/test.tmp)
message(SEND_ERROR "${test} - FAILED:\n"
"test.tmp was not deleted")
endif()
run_cmake_command(llvm_rc_full_run ${CMAKE_COMMAND} -E cmake_llvm_rc test.tmp ${CMAKE_COMMAND} -E echo "This is a test" -- ${CMAKE_COMMAND} -E copy test.tmp llvmrc.result )
if(EXISTS ${RunCMake_TEST_BINARY_DIR}/test.tmp)
message(SEND_ERROR "${test} - FAILED:\n"
"test.tmp was not deleted")
endif()
file(READ ${RunCMake_TEST_BINARY_DIR}/llvmrc.result LLVMRC_RESULT)
if(NOT "${LLVMRC_RESULT}" STREQUAL "This is a test\n")
message(SEND_ERROR "${test} - FAILED:\n"
"llvmrc.result was not created")
endif()
# file(REMOVE ${RunCMake_TEST_BINARY_DIR}/llvmrc.result)
unset(LLVMRC_RESULT)
endfunction()
run_llvm_rc()

@ -0,0 +1 @@
Empty preprocessing command

@ -0,0 +1 @@
Empty resource compilation command

@ -0,0 +1 @@
Invalid cmake_llvm_rc arguments