4

I'm using glslc to compile GLSL shaders with #includes (not part of the core spec IIRC but supported in shaderc, which is the engine behind glslc, distributed with the LunarG Vulkan SDK) into SPIR-V for Vulkan and GL 4.5. glslc emits gcc-style depsfiles ([my_shader].[ext].d) files containing dependency info.

My project is built with cmake/ninja/MSVC 2017.

Today, I use a cmake custom_command to invoke glslc when a shader has changed on disk, as a post-build step to my primary target. However, this doesn't catch the changes in included files (isn't at all aware of the .d files or their contents), so rebuilding shaders when an included glsl file is changed can trip up myself and other people on my team.

It looks like ninja can invoke arbitrary compilers, and since ninja knows how to handle the depsfiles, I should be able to coerce ninja into running glslc -- unsure about other build systems since right now we're standardized on ninja.

So how can I tell cmake to configure ninja to use glslc for a specific target? Or is there a paradigmatic way to get this done? It looks like a cmake pull request to add support for glslc as a compiler didn't make it in to cmake circa 2016, so whatever I do is going to be a workaround.

Alterscape
  • 1,526
  • 1
  • 17
  • 34

1 Answers1

8

CMake understands depfiles when used in conjunction with ninja.

DEPFILE

Specify a .d depfile for the Ninja generator. A .d file holds dependencies usually emitted by the custom command itself. Using DEPFILE with other generators than Ninja is an error.

add_custom_command(
    OUTPUT ${source}.h
    DEPENDS ${source}
    COMMAND
        glslc
        -MD -MF ${source}.d
        -o ${source}.h -mfmt=num
        --target-env=opengl
        ${CMAKE_CURRENT_SOURCE_DIR}/${source}
    DEPFILE ${source}.d
)
  -M                Generate make dependencies. Implies -E and -w.
  -MM               An alias for -M.
  -MD               Generate make dependencies and compile.
  -MF <file>        Write dependency output to the given file.
  -MT <target>      Specify the target of the rule emitted by dependency
                    generation.

EDIT: Getting fancier

find_package(Vulkan COMPONENTS glslc)
find_program(glslc_executable NAMES glslc HINTS Vulkan::glslc)

function(compile_shader target)
    cmake_parse_arguments(PARSE_ARGV 1 arg "" "ENV;FORMAT" "SOURCES")
    foreach(source ${arg_SOURCES})
        add_custom_command(
            OUTPUT ${source}.${arg_FORMAT}
            DEPENDS ${source}
            DEPFILE ${source}.d
            COMMAND
                ${glslc_executable}
                $<$<BOOL:${arg_ENV}>:--target-env=${arg_ENV}>
                $<$<BOOL:${arg_FORMAT}>:-mfmt=${arg_FORMAT}>
                -MD -MF ${source}.d
                -o ${source}.${arg_FORMAT}
                ${CMAKE_CURRENT_SOURCE_DIR}/${source}
        )
        target_sources(${target} PRIVATE ${source}.${arg_FORMAT})
    endforeach()
endfunction()
add_executable(dummy dummy.c)
compile_shader(dummy
    ENV opengl
    FORMAT num
    SOURCES
        dummy.vert
        dummy.frag
)
diapir
  • 2,872
  • 1
  • 19
  • 26
  • ```CMake Error at src/GUI_glfw3/vulkan/CMakeLists.txt:23 (target_sources): Cannot specify sources for target "dummy" which is not built by this project. Call Stack (most recent call first): src/GUI_glfw3/vulkan/CMakeLists.txt:27 (compile_shader) ``` is there a more complete example somewhere on how to use this? – Carlo Wood Jul 16 '21 at 13:47
  • @CarloWood You're missing a target. I've expanded the usage example. – diapir Sep 01 '21 at 21:49
  • This does not appear to work, while I don't get cmake errors, changing dependencies in include files does not initiate a re-compile of the shaders. The .d files are indeed spawned correctly. – Krupip Feb 05 '22 at 22:35
  • It seems that this change: "Changed in version 3.13: Relative source file paths are interpreted as being relative to the current source directory (i.e. CMAKE_CURRENT_SOURCE_DIR). See policy CMP0076." would indicate that target_sources should read: target_sources(${target} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/${source}.${arg_FORMAT}) See https://cmake.org/cmake/help/latest/command/target_sources.html – David Bien Feb 26 '22 at 00:04
  • Cheers for this, @diapir. This means I don't have to use the leaky-as-hell shaderc code in my own, but I still get sensible compilations upon changes. =) – pdm Aug 24 '22 at 02:21