We are testing a project setup that requires us to include or exclude a source file based on the compiler's version. The test project is located on GitHub at CRC-Test. For the PoC, we are developing the process with CRC-32C since both Intel and ARM have it. Later it will be applied to AES, CLMUL, SHA, AVX{2}, BMI{2}, ARMv7a NEON, ARMv8, etc. (MS compilers support NEON and ARMv8 through intrinsics).
The source file I am trying to conditionally compile is crc-simd.cpp
. It has implementations using SSE4.2 intrinsics and ARMv8 intrinsics (crc.cpp
provides the generic, C++ implementation).
I added a VCX Project file at crc-test.vcxproj. There is an ItemGroup
for ClCompile
:
<ItemGroup>
<ClCompile Include="main.cpp" >
<PrecompiledHeader />
</ClCompile>
<ClCompile Include="crc.cpp" >
<PrecompiledHeader />
</ClCompile>
<ClCompile Include="crc-simd.cpp" >
<PrecompiledHeader />
</ClCompile>
</ItemGroup>
I need a conditional for <ClCompile Include="crc-simd.cpp" >
that says, "if cl.exe
is 15.00 or greater, then include the source file". In the preprocessor I would check for _MSC_VER >= 1500
. In MSBuild I think I am looking for something like:
<ItemGroup Condition="'$(CL_VERSION)'>='15.00'" Label="SSE4.2 acceleration">
<ClCompile Include="crc-simd.cpp" >
<PrecompiledHeader />
</ClCompile>
</ItemDefinitionGroup>
I found List of MSBuild built-in variables, but I don't know what $(CL_VERSION)
is in real life. Seraching for "cl" did not result in interesting hits. I found one question at MSBuild vs compiler, but its asking a different question.
My question is, how do I reference the cl.exe
version in a MSBuild conditional?
A related problem is, sometimes I need the full version. For example, AES support was added at VS2005 SP1, cl.exe
version 15.00.30729 (_MSC_FULL_VER >= 150030729
). My related question is, how do I reference the cl.exe
full version in a MSBuild conditional?
Trying to use the following from a developer prompt:
<ItemDefinitionGroup Condition="'$(CL_VERSION)'>='15.00'" Label="SSE4.2 acceleration">
<ClCompile Include="crc-simd.cpp" >
<PrecompiledHeader />
</ClCompile>
</ItemDefinitionGroup>
Results in:
> cls && msbuild
Microsoft (R) Build Engine version 4.6.1087.0
[Microsoft .NET Framework, version 4.0.30319.42000]
Copyright (C) Microsoft Corporation. All rights reserved.
Build started 7/25/2017 1:23:31 PM.
Project "C:\Users\Test\CRC-Test\crc-test.vcxproj" on node 1 (default ta
rgets).
C:\Users\Test\CRC-Test\crc-test.vcxproj(127,14): error MSB4066: The att
ribute "Include" in element <ClCompile> is unrecognized.
Done Building Project "C:\Users\Test\CRC-Test\crc-test.vcxproj" (defaul
t targets) -- FAILED.
Build FAILED.
"C:\Users\Test\CRC-Test\crc-test.vcxproj" (default target) (1) ->
C:\Users\Test\CRC-Test\crc-test.vcxproj(127,14): error MSB4066: The a
ttribute "Include" in element <ClCompile> is unrecognized.
0 Warning(s)
1 Error(s)
Time Elapsed 00:00:00.11
The project is currently setup for Visual Studio 2010. If you using a different version, then change this to suite your tastes. There is no need for VCUpgrade
:
<PlatformToolset>v100</PlatformToolset>
- VS2010 → v100
- VS2012 → v110
- VS2013 → v120
- VS2015 → v130
- VS2017 → v140
Here is the essence of the project on Linux in a GNUmakefile
. When the compiler and platform support hardware acceleration, we bring-in the crc-simd.cpp
file for the faster implementation. Its what we want to do with MSBuild.
HAS_ARMV8 ?= $(shell uname -m | $(EGREP) -i -c 'aarch32|aarch64')
HAS_SSE42 ?= $(shell uname -m | $(EGREP) -i -c 'amd64|x86_64|i*86')
GCC43_OR_LATER := $(shell $(CXX) -v 2>&1 | $(EGREP) -i -c "gcc version (4\.[3-9]|[5-9]\.)")
GCC48_OR_LATER := $(shell $(CXX) -v 2>&1 | $(EGREP) -i -c "gcc version (4\.[8-9]|[5-9]\.)")
...
OBJECTS = main.o crc.o
ifeq ($(HAS_ARMV8)$(GCC48_OR_LATER),11)
OBJECTS += crc-simd.o
endif
ifeq ($(HAS_SSE42)$(GCC43_OR_LATER),11)
OBJECTS += crc-simd.o
endif
...
ifeq ($(HAS_ARMV8)$(GCC48_OR_LATER),11)
crc-simd.o : crc-simd.cpp
$(CXX) $(CXXFLAGS) -march=armv8-a+crc -c $<
endif
ifeq ($(HAS_SSE42)$(GCC43_OR_LATER),11)
crc-simd.o : crc-simd.cpp
$(CXX) $(CXXFLAGS) -msse4.2 -c $<
endif
...
A similar approach as for makefiles can be used: run cl.exe, use a regex to get the version, include source file if version is greater than some number. In code:
Notes: the lists of built-in variables you found are for msbuild, the build system, which is sort of seperate from the compiler; that's why there's no CL_VERSION just like a makefile has no GCC_VERSION by default. Also it's worth trying to use
instead of sticking to a single one or keeping multiple projects, one for each VS version. It also keeps VS from asking to upgrade (since it sees it's default platform toolset being used). I don't have VS versions < 2012 to test if it works there though, but it does from VS2012 and onwards so that's already 4 of them, and counting.