diff --git a/icu4c/source/common/unicode/platform.h b/icu4c/source/common/unicode/platform.h index 7aca76c67db8..59176005f334 100644 --- a/icu4c/source/common/unicode/platform.h +++ b/icu4c/source/common/unicode/platform.h @@ -235,7 +235,7 @@ /** * \def U_PLATFORM_USES_ONLY_WIN32_API * Defines whether the platform uses only the Win32 API. - * Set to 1 for Windows/MSVC and MinGW but not Cygwin. + * Set to 1 for Windows/MSVC, ClangCL and MinGW but not Cygwin. * @internal */ #ifdef U_PLATFORM_USES_ONLY_WIN32_API @@ -250,7 +250,7 @@ /** * \def U_PLATFORM_HAS_WIN32_API * Defines whether the Win32 API is available on the platform. - * Set to 1 for Windows/MSVC, MinGW and Cygwin. + * Set to 1 for Windows/MSVC, ClangCL, MinGW and Cygwin. * @internal */ #ifdef U_PLATFORM_HAS_WIN32_API diff --git a/icu4c/source/tools/genccode/genccode.c b/icu4c/source/tools/genccode/genccode.c index 9fb7dbcdf2eb..0f243952a7d4 100644 --- a/icu4c/source/tools/genccode/genccode.c +++ b/icu4c/source/tools/genccode/genccode.c @@ -70,6 +70,7 @@ enum { #ifdef CAN_GENERATE_OBJECTS kOptObject, kOptMatchArch, + kOptCpuArch, kOptSkipDllExport, #endif kOptFilename, @@ -86,6 +87,7 @@ static UOption options[]={ #ifdef CAN_GENERATE_OBJECTS /*6*/UOPTION_DEF("object", 'o', UOPT_NO_ARG), UOPTION_DEF("match-arch", 'm', UOPT_REQUIRES_ARG), + UOPTION_DEF("cpu-arch", 'c', UOPT_REQUIRES_ARG), UOPTION_DEF("skip-dll-export", '\0', UOPT_NO_ARG), #endif UOPTION_DEF("filename", 'f', UOPT_REQUIRES_ARG), @@ -131,6 +133,8 @@ main(int argc, char* argv[]) { "\t-o or --object write a .obj file instead of .c\n" "\t-m or --match-arch file.o match the architecture (CPU, 32/64 bits) of the specified .o\n" "\t ELF format defaults to i386. Windows defaults to the native platform.\n" + "\t-c or --cpu-arch Specify a CPU architecture for which to write a .obj file for ClangCL on Windows\n" + "\t Valid values for this opton are x64, x86 and arm64.\n" "\t--skip-dll-export Don't export the ICU data entry point symbol (for use when statically linking)\n"); #endif fprintf(stderr, @@ -193,9 +197,17 @@ main(int argc, char* argv[]) { break; #ifdef CAN_GENERATE_OBJECTS case CALL_WRITEOBJECT: + if(options[kOptCpuArch].doesOccur) { + if (!checkCpuArchitecture(options[kOptCpuArch].value)) { + fprintf(stderr, + "CPU architecture \"%s\" is unknown.\n", options[kOptCpuArch].value); + return -1; + } + } writeObjectCode(filename, options[kOptDestDir].value, options[kOptEntryPoint].doesOccur ? options[kOptEntryPoint].value : NULL, options[kOptMatchArch].doesOccur ? options[kOptMatchArch].value : NULL, + options[kOptCpuArch].doesOccur ? options[kOptCpuArch].value : NULL, options[kOptFilename].doesOccur ? options[kOptFilename].value : NULL, NULL, 0, diff --git a/icu4c/source/tools/pkgdata/pkgdata.cpp b/icu4c/source/tools/pkgdata/pkgdata.cpp index 16626f64b0e0..b34618c716be 100644 --- a/icu4c/source/tools/pkgdata/pkgdata.cpp +++ b/icu4c/source/tools/pkgdata/pkgdata.cpp @@ -776,6 +776,7 @@ static int32_t pkg_executeOptions(UPKGOptions *o) { o->entryName, (optMatchArch[0] == 0 ? nullptr : optMatchArch), nullptr, + nullptr, gencFilePath, sizeof(gencFilePath), true); diff --git a/icu4c/source/tools/toolutil/pkg_genc.cpp b/icu4c/source/tools/toolutil/pkg_genc.cpp index 39b479a66051..c33343318b3d 100644 --- a/icu4c/source/tools/toolutil/pkg_genc.cpp +++ b/icu4c/source/tools/toolutil/pkg_genc.cpp @@ -16,6 +16,9 @@ # define NOMCX #include #include +# if defined(__clang__) +# include +# endif # ifdef __GNUC__ # define WINDOWS_WITH_GNUC # endif @@ -294,6 +297,11 @@ checkAssemblyHeaderName(const char* optAssembly) { return false; } +U_CAPI UBool U_EXPORT2 +checkCpuArchitecture(const char* optCpuArch) { + return strcmp(optCpuArch, "x64") == 0 || strcmp(optCpuArch, "x86") == 0 || strcmp(optCpuArch, "arm64") == 0; +} + U_CAPI void U_EXPORT2 printAssemblyHeadersToStdErr() { @@ -799,7 +807,12 @@ getOutFilename( #ifdef CAN_GENERATE_OBJECTS static void -getArchitecture(uint16_t *pCPU, uint16_t *pBits, UBool *pIsBigEndian, const char *optMatchArch) { +getArchitecture( + uint16_t *pCPU, + uint16_t *pBits, + UBool *pIsBigEndian, + const char *optMatchArch, + [[maybe_unused]] const char *optCpuArch) { union { char bytes[2048]; #ifdef U_ELF @@ -847,7 +860,25 @@ getArchitecture(uint16_t *pCPU, uint16_t *pBits, UBool *pIsBigEndian, const char # if defined(_M_IX86) *pCPU = IMAGE_FILE_MACHINE_I386; # else - *pCPU = IMAGE_FILE_MACHINE_UNKNOWN; + // Linker for ClangCL doesn't handle IMAGE_FILE_MACHINE_UNKNOWN the same as + // linker for MSVC. Because of this optCpuArch is used to define the CPU + // architecture in that case. While _M_AMD64 and _M_ARM64 could be used, + // this would potentially be problematic when cross-compiling as this code + // would most likely be ran on host machine to generate the .obj file for + // the target architecture. +# if defined(__clang__) + if (strcmp(optCpuArch, "x64") == 0) { + *pCPU = IMAGE_FILE_MACHINE_AMD64; + } else if (strcmp(optCpuArch, "x86") == 0) { + *pCPU = IMAGE_FILE_MACHINE_I386; + } else if (strcmp(optCpuArch, "arm64") == 0) { + *pCPU = IMAGE_FILE_MACHINE_ARM64; + } else { + std::terminate(); // Unreachable. + } +# else + *pCPU = IMAGE_FILE_MACHINE_UNKNOWN; +# endif # endif # if defined(_M_IA64) || defined(_M_AMD64) || defined (_M_ARM64) *pBits = 64; // Doesn't seem to be used for anything interesting though? @@ -934,6 +965,7 @@ writeObjectCode( const char *destdir, const char *optEntryPoint, const char *optMatchArch, + const char *optCpuArch, const char *optFilename, char *outFilePath, size_t outFilePathCapacity, @@ -1201,7 +1233,7 @@ writeObjectCode( #endif /* deal with options, files and the entry point name */ - getArchitecture(&cpu, &bits, &makeBigEndian, optMatchArch); + getArchitecture(&cpu, &bits, &makeBigEndian, optMatchArch, optCpuArch); if (optMatchArch) { printf("genccode: --match-arch cpu=%hu bits=%hu big-endian=%d\n", cpu, bits, makeBigEndian); diff --git a/icu4c/source/tools/toolutil/pkg_genc.h b/icu4c/source/tools/toolutil/pkg_genc.h index f811fe532349..ef966aba1306 100644 --- a/icu4c/source/tools/toolutil/pkg_genc.h +++ b/icu4c/source/tools/toolutil/pkg_genc.h @@ -73,6 +73,9 @@ printAssemblyHeadersToStdErr(void); U_CAPI UBool U_EXPORT2 checkAssemblyHeaderName(const char* optAssembly); +U_CAPI UBool U_EXPORT2 +checkCpuArchitecture(const char* optCpuArch); + U_CAPI void U_EXPORT2 writeCCode( const char *filename, @@ -98,6 +101,7 @@ writeObjectCode( const char *destdir, const char *optEntryPoint, const char *optMatchArch, + const char *optCpuArch, const char *optFilename, char *outFilePath, size_t outFilePathCapacity,