Unable to fully build Windows UMDF Driver Using CMake

Hello everyone,

I’m running into issues trying to compile a Windows UMDF driver using CMake version 3.18+. More specifically, I am able to compile, but run into a strange ZERO_CHECK error that causes the build to fail.

Within my CMakeLists.txt, I call a helper method wdk_add_library(), which looks as follows:

function(wdk_add_library _target)
    cmake_parse_arguments(WDK "" "UMDF;WINVER" "" ${ARGN})

    add_library(${_target} ${WDK_UNPARSED_ARGUMENTS})
    set_target_properties(${_target} PROPERTIES VS_PLATFORM_TOOLSET "WindowsUserModeDriver10.0")
    set_target_properties(${_target} PROPERTIES COMPILE_OPTIONS "${WDK_COMPILE_FLAGS}")
    set_target_properties(${_target} PROPERTIES COMPILE_DEFINITIONS 
        "${WDK_COMPILE_DEFINITIONS};$<$<CONFIG:Debug>:${WDK_COMPILE_DEFINITIONS_DEBUG};_WIN32_WINNT=${WDK_WINVER}>"
        )
    set_target_properties(${_target} PROPERTIES LINK_FLAGS "${WDK_LINK_FLAGS}")

    target_include_directories(${_target} SYSTEM PRIVATE
        "${WDK_ROOT}/Include/${WDK_VERSION}/shared"
        "${WDK_ROOT}/Include/${WDK_VERSION}/um"
        )

    target_link_libraries( ${_target} WDK::NTDLL WDK::MINCORE )

    if(DEFINED WDK_UMDF)
        target_include_directories(${_target} SYSTEM PRIVATE "${WDK_ROOT}/Include/wdf/umdf/${WDK_UMDF}")
        target_link_libraries(${_target}
            "${WDK_ROOT}/Lib/wdf/umdf/${WDK_PLATFORM}/${WDK_UMDF}/WdfDriverStubUm.lib"
            )
    endif()
endfunction()

I’m basing this off of a FindWDK.cmake module created here, which has been used for KMDF driver purposes. I have changed stuff around to detect the associated UMDF libraries and includes, rather than the KMDF ones.

I am calling wdk_add_library() with a SHARED type, as Windows UMDF drivers are DLLs.

The source files of my driver don’t seem to be the issue here - they compile fine and can be properly used in a template UMDF Visual Studio project. The issue occurs when ZERO_CHECK is called (after compilation):

I have spent a bit of time looking for this target definition in other solution files, as well as references to it on search engines (hoping it would be a known/expected target in a Visual Studio vcxproj). However, I haven’t been able to find it quite yet.

I should note that the issue disappears if I build my library as a STATIC one. However, I don’t believe I can really move forward at that stage, as the rest of the driver process expects a dll.

Is anyone familiar with building UMDF drivers using CMake able to point out what I am doing wrong? I’m sorry if this is quite obvious!

Thank you,
Nick

I’m not sure exactly what’s going on here, but I’ll note that I think it’d be better to use the target_ family of commands rather than setting their properties directly:

  • COMPILE_OPTIONStarget_compile_options
  • COMPILE_DEFINITIONStarget_compile_definitions
  • LINK_FLAGStarget_link_options

Back to the actual question, does GetDriverProjectAttributes show up in your project at all?

Cc: @brad.king in case you’ve seen something like this before.

For reference, the VS_PLATFORM_TOOLSET target property (mentioned in the example above) was added by CMake MR 4682 specifically to support driver mode builds.

Thanks for the quick response, Ben and Brad. I updated to use target_XXX calls where applicable (as far as I can tell, this cannot be done for VS_PLATFORM_TOOLSET). As expected, the problem remains.

This call does not show up anywhere in my project, nor does it show up in the vcxproj files. As far as I can decipher, whatever logic was added for “VS_PLATFORM_TOOLSET == WindowsUserModeDriver10.0” seems to be expecting the created vcxproj to contain a GetDriverProjectAttributes target? From perusing the Visual Studio vcxproj / sln file documentation, I don’t see any reference to anything like this, though.

I should clarify that I have tried this both with MSVC++ compiler v141 and v142, and that I’m trying this using Visual Studio 2019, with Windows SDK 10.0.18362.0. The compilation and linking seem fine, though - this seems to really be some byproduct of my settings causing the ZERO_CHECK project to look for something that is not there…

Thanks again for any help.

Hmm. We might need to add something to the vcxproj files according to this commit: https://gitlab.freedesktop.org/spice/win32/usbdk/-/commit/2cd4aa5262fd293c3e512961f869dee5d40b28d1#bf28dd91e2de29de3b2fa70d6541e552e4166b69_1038_372

Hi Ben,

I’m wondering if this is some older vcxproj attribute - although I don’t see it in Microsoft documentation, they have a tendency to flush and redo their documentation every couple years (or at least, misplace hyperlinks). The target “GetDriverProjectAttributes” does not show up in the sample UMDF driver I am comparing against (created by selecting a new sample UMDF 2 Driver using Visual Studio), nor does it in a separate UMDF driver project I am also checking. I would upload either here, but do not yet have the rights to do so.

If that’s the case, wouldn’t the situation be the other way around? Whatever logic is getting called during ZERO_CHECK is checking that the associated .vcxproj has a target “GetDriverProjectAttributes”, when this is not necessary?

I’ve tried grep-ing around in the share/Modules portion of my CMake install, to see if I could find any references to this. No luck thus far…

Thanks,
Nick

CMake’s source code doesn’t mention it either, so figuring out what ends up wanting it would be useful. Can you reproduce it with a minimal example that you could upload?

Sure thing. I am not able to upload files directly here (as a new user), but I’ve uploaded a sample to Google Drive. See here: https://drive.google.com/file/d/1hE0VSUc8-EKMMz48vBZMKL-Uad4iGs-f/view?usp=sharing

A couple notes:

Let me know if you run into any issues! Thanks again.

Hi!

As Ben mentiond before, adding the “GetDriverProjectAttributes” and “GetPackageFiles” manually for all dependend .vcxproj did the trick. I’ve compared the cmake-generated against my UMDF project file (as this is build without errors) to identify a potential setting that may causes this issue with no success.

I’m suspecting the -tag itself to be the problem here, as the error occures while the driver is building complaining that the Tags are missing in the depended project. Here is a potential related issue I found: https://github.com/dotnet/msbuild/issues/2874 where adding something to the project reference bypasses the problem. But I’m also stuck at this point.

Any progress on your side?

Hi Myon,

I wasn’t able to get past the issue, to be honest. I ended up working around it for now by using include_external_msproject(). Certainly not the best solution, but I was stuck at that point…

When you say: “adding the “GetDriverProjectAttributes” and “GetPackageFiles” manually for all dependend .vcxproj did the trick”, do you mean that manually adding these to the generated files allows the project to compile? Again, I didn’t test this because my sample/comparison UMDF driver did not have them (I had no sample for what they should look like).

Did anyone ever get to the bottom of what was causing this error and how to resolve?

I’m actually getting the error without cmake; using solely Visual Studio generated MSBuild vcxproj files.

Figured I’d add that as a datapoint here and also hopeful that someone found a solution.

Visual Studio requires all dependencies to have Targets “GetDriverProjectAttributes” and “GetPackageFiles”. So I added them inside the node for ZERO_CHECK and my two other dependencies by hand:

  <Target Name="GetDriverProjectAttributes" Returns="@(DriverProjectAttributes)"/>
  <Target Name="GetPackageFiles" Returns="@(FullyQualifiedFilesToPackage)"/>

For automation I created an ugly script “FixProjects.cmake” that just injects thes two lines at line 2:

file(READ "${PATH}" contents)

STRING(REGEX REPLACE ";" "#SEMICOLON#" contents "${contents}")
STRING(REGEX REPLACE "\n" ";" contents "${contents}")

LIST(GET contents 2 line_2)
LIST(GET contents 3 line_3)

set(miss_1 "  <Target Name=\"GetDriverProjectAttributes\" Returns=\"@(DriverProjectAttributes)\"/>")
set(miss_2 "  <Target Name=\"GetPackageFiles\" Returns=\"@(FullyQualifiedFilesToPackage)\"/>")

if ((NOT ${line_2} STREQUAL ${miss_1}) OR (NOT ${line_3} STREQUAL ${miss_2}))
	LIST(INSERT contents 2 ${miss_1} ${miss_2})

	STRING(REGEX REPLACE ";" "\n" contents "${contents}")
	STRING(REGEX REPLACE "#SEMICOLON#" ";" contents "${contents}")
	
	file(WRITE "${PATH}" "${contents}")
	message("Fixed Project-File: ${PATH}")
endif()

In your CMakeLists.txt just add the folowing custom command:

add_custom_command(
	TARGET ${PROJECT_NAME}
	PRE_BUILD
	COMMAND ${CMAKE_COMMAND} -D "PATH=\"${CMAKE_BINARY_DIR}/ZERO_CHECK.vcxproj\"" -P "${CMAKE_CURRENT_SOURCE_DIR}/FixProjects.cmake"
	COMMAND ${CMAKE_COMMAND} -D "PATH=\"${CMAKE_BINARY_DIR}/<path-to-any-other-dependency>.vcxproj\"" -P "${CMAKE_CURRENT_SOURCE_DIR}/FixProjects.cmake"
)

Not pretty but it works for me