Conditionally include source file based on compile

2019-06-26 09:27发布

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
...

1条回答
Lonely孤独者°
2楼-- · 2019-06-26 09:51

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:

<Target Name="GetClVersion">
  <!-- Run cl, store output in variable -->
  <Exec Command="cl.exe" ConsoleToMSBuild="True" IgnoreExitCode="True">
    <Output TaskParameter="ConsoleOutput" PropertyName="ClOut" />
  </Exec>
  <PropertyGroup>
    <!-- cl.exe version number is something like 18.00.31101 -->
    <ClVersion>$([System.Text.RegularExpressions.Regex]::Match('$(ClOut)','(\d+\.\d+\.\d+)'))</ClVersion>
    <!-- Turn it into an integer like 180031101 by getting rid of dots -->
    <ClVersion>$([System.String]::Copy('$(ClVersion)').Replace('.', ''))</ClVersion>
  </PropertyGroup>
</Target>

<!-- This will run early in the build, before compilation,
     so we can modify the ClCompile ItemGroup. Needs to be done
     in a target, else we cannot use results from other targets
     (GetClVersion in this case) -->
<Target Name="ShowClVersion" DependsOnTargets="GetClVersion"
        BeforeTargets="BuildGenerateSources">
  <!-- &gt; = escaped > sign -->
  <ItemGroup Condition="$(ClVersion) &gt; 180031100">
    <ClCompile Include="somefile.cpp" />
  </ItemGroup>
</Target>

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

<PlatformToolset>$(DefaultPlatformToolset)</PlatformToolset>

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.

查看更多
登录 后发表回答