#----------------------------------------------------------------
# Syntax/Formatting Conventions:
#
#  CMake keywords and vars    : uppercase
#  HSE global vars            : uppercase
#  Local vars                 : lowercase
#  Function parameter FOO     : param_FOO
#----------------------------------------------------------------

include(CMakeParseArguments)

function(my_join output sep)
    set(result "")
    set(esep "") # effective sep is empty at the beginning
    foreach(arg ${ARGN})
        set(result "${result}${esep}${arg}")
        set(esep "${sep}")
    endforeach()
    set(${output} "${result}" PARENT_SCOPE)
endfunction()

#
# Functions that add text to the beginning of each element of a list
#
# The action of each of these is given in terms of their operation on
# the variable foo defined as:
#
#     SET( foo /tmp/cat /tmp/dog )
#

#
# my_add_prefix(output " -I" ${foo})  ==>  ${output}=="-I/tmp/cat; -I/tmp/dog"
#
function(my_add_prefix output_var text)
    set(result "")
    foreach(arg ${ARGN})
        list(APPEND result "${text}${arg}")
    endforeach()
    set(${output_var} "${result}" PARENT_SCOPE)
endfunction()

#
# my_prepend_over(output " -I" ${foo})  ==>  ${output}=="-I/tmp/cat -I/tmp/dog"
#
function(my_prepend_over output pfx)
    set(result "")
    foreach(arg ${ARGN})
        set(result "${result}${pfx}${arg}")
    endforeach()
    set(${output} "${result}" PARENT_SCOPE)
endfunction()

function(my_add_suffix output_var text)
    set(result "")
    foreach(arg ${ARGN})
        list(APPEND result "${arg}${text}")
    endforeach()
    set(${output_var} "${result}" PARENT_SCOPE)
endfunction()

function(my_execute_or_die)
    execute_process(
        COMMAND ${ARGN}
        ERROR_VARIABLE  cmd_stderr
        RESULT_VARIABLE cmd_status)
    if(NOT("${cmd_status}" STREQUAL "0"))
        my_join(cmd " " ${ARGN})
        message(FATAL_ERROR
            "Error executing command\n"
            "Command: ${cmd}\n"
            "Exit Status: ${cmd_status}\n"
            "Stderr: ${cmd_stderr}\n")
    endif()
endfunction()

function(my_ut_preproc_file in_file output_dir result_name)

    get_filename_component( in_file_base ${in_file} NAME_WE )
    set( output_hdr ${in_file_base}_ut.h )
    set( output_src ${in_file_base}_ut_impl.i )
    set( output_set ${output_hdr} ${output_src} )

    my_execute_or_die(mkdir -p ${output_dir})

    add_custom_command(
        OUTPUT ${output_dir}/${output_hdr}
        COMMAND ${UTPP} -- -h -o ${output_dir}/${output_hdr} ${in_file}
        DEPENDS ${in_file}
        )

    add_custom_command(
        OUTPUT ${output_dir}/${output_src}
        COMMAND ${UTPP} -- -c -o ${output_dir}/${output_src} ${in_file}
        DEPENDS ${in_file}
        )

    set( ${result_name}
        "${output_dir}/${output_hdr}"
        "${output_dir}/${output_src}" PARENT_SCOPE )

endfunction()

#----------------------------------------------------------------
# Use my_copy_files to copy a set of files from the current src
# dir to the corresponding build output dir:
#
#   my_copy_files(foobar
#    ${CMAKE_CURRENT_BINARY_DIR}
#     readme *.txt *.json *.sh doc/*.txt)
#----------------------------------------------------------------

macro(my_copy_files target_name dst_dir)
    add_custom_target(${target_name} ALL)
    foreach(PATTERN ${ARGN})
        file(GLOB COPY_FILES
            RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
            ${PATTERN})
        foreach(filename ${COPY_FILES})
            set(SRC "${CMAKE_CURRENT_SOURCE_DIR}/${filename}")
            set(DST "${dst_dir}/${filename}")
            add_custom_command(
                TARGET ${target_name}
                COMMAND ${CMAKE_COMMAND} -E copy_if_different ${SRC} ${DST})
        endforeach()
    endforeach()
endmacro()

#----------------------------------------------------------------
# Usage:
#    hse_unit_test(
#        NAME kvblock_test
#      [ EXECUTABLE kvblock_test ]
#      [ COMMAND kvblock_test --test ]`
#        SRCS kvblock_test.cpp ...
#      [ DEPS      ... ]
#      [ LINK_DIRS ... ]
#      [ LINK_LIBS ... ]
#      [ LABELS    ... ])
#
# Notes:
#  - COMMAND is optional, it defaults to NAME
#  - DEPS and LINK_LIBS are all optional
#
# Examples:
#    hse_unit_test(
#        NAME foo_test
#        SRCS foo_test.cpp foo.c)
#
#    hse_unit_test(
#        NAME bar_test
#        SRCS bar_test.cpp bar_test_lib.c
#        LINK_LIBS hse_util)
#
#----------------------------------------------------------------

function(hse_unit_test)

    set(one_value_opts
        NAME            # (required) name for unit test
        EXECUTABLE      # (optional) name for unit test executable
        )

    set(multi_value_opts
        SRCS            # (required) list of source files
        COMMAND         # (optional) command to run to execute unit test
        DEPS            # (optional) targets this depends on
        CFLAGS          # (optional) Additional CFLAGS for this target
        INCLUDES        # (optional) Directories to search for #include
        LINK_DIRS       # (optional) list of directories to be searched for libs
        LINK_LIBS       # (optional) list of libs to link
        LABELS          # (optional) labels for the unit test
        )

    cmake_parse_arguments(
        param
        "" "${one_value_opts}" "${multi_value_opts}"
        ${ARGN})


    if("${param_EXECUTABLE}" STREQUAL "")
        set(param_EXECUTABLE ${param_NAME})
    endif()

    if("${param_COMMAND}" STREQUAL "")
        set(param_COMMAND ${param_EXECUTABLE})
    endif()

    add_test(
        NAME    ${param_NAME}
        COMMAND ${param_COMMAND})

    set_property(TEST ${param_NAME}
        PROPERTY LABELS "user_unit;${HSE_TEST_LABELS};${param_LABELS}")

    add_executable(${param_EXECUTABLE} ${param_SRCS})

    # tell the laoder to look in the right place for the mpool library
    if("${DEV_MPOOL_LIB_DIR}" STREQUAL "true")
        set_property(TARGET ${param_EXECUTABLE}
            APPEND_STRING PROPERTY LINK_FLAGS "-Wl,-rpath,${MPOOL_LIB_DIR} ")
    endif()

    # tell the laoder to look in the right place for the mpool library
    if("${DEV_BLKID_LIB_DIR}" STREQUAL "true")
        set_property(TARGET ${param_EXECUTABLE}
            APPEND_STRING PROPERTY LINK_FLAGS "-Wl,-rpath,${BLKID_LIB_DIR} ")
    endif()

    set_property(TARGET ${param_EXECUTABLE}
        APPEND_STRING PROPERTY COMPILE_FLAGS ${param_CFLAGS})

    set_property(TARGET ${param_EXECUTABLE}
        PROPERTY RUNTIME_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/utest)

    if(NOT "${param_INCLUDES}" STREQUAL "")
        target_include_directories(${param_EXECUTABLE} PRIVATE ${param_INCLUDES})
    endif()

    if(NOT "${param_DEPS}" STREQUAL "")
        add_dependencies(${param_EXECUTABLE} ${param_DEPS})
    endif()

    if(NOT "${param_LINK_LIBS}" STREQUAL "")
        target_link_libraries(${param_EXECUTABLE} ${param_LINK_LIBS})
    endif()

    target_compile_options(${param_EXECUTABLE} PRIVATE "-DHSE_UNIT_TEST_MODE=1" )
endfunction()

#----------------------------------------------------------------
# hse_executable : Function to build an executable.
#
# Usage:
#
#    hse_executable(
#        NAME      foobar
#        SRCS      foo.c bar.c
#        LINK_LIBS hse_platform m
#        [OUTDIR dir]
#        [DESTINATION dir]
#        [COMPONENT component])
#
# DESTINATION is optional.  It defaults to "bin".
# COMPONENT is optional.  It defaults to "runtime".
#----------------------------------------------------------------

function(hse_executable)

    set(one_value_opts
        NAME            # (required) name for collection of object files
        COMPONENT       # (optional) installation component
        DESTINATION     # (optional) installation directory
        OUTDIR          # (optional) output directory
        )

    set(multi_value_opts
        SRCS            # (required) list of source files
        DEPS            # (optional) targets this depends on
        CFLAGS          # (optional) Additional CFLAGS for this target
        INCLUDES        # (optional) Directories to search for #include
        LINK_DIRS       # (optional) list of directories to be searched for libs
        LINK_LIBS       # (optional) list of libs to link
        )

    cmake_parse_arguments(
        param
        "" "${one_value_opts}" "${multi_value_opts}"
        ${ARGN})

    if("${param_NAME}" STREQUAL "")
        message(FATAL_ERROR "HSE_EXECUTABLE: Must set NAME")
    endif()

    if("${param_SRCS}" STREQUAL "")
        message(FATAL_ERROR "HSE_EXECUTABLE: Must set SRCS")
    endif()

    # Optional DESTINATION defaults to "bin"
    if("${param_DESTINATION}" STREQUAL "")
        set(param_DESTINATION "bin")
    endif()

    if(NOT "${param_UNPARSED_ARGUMENTS}" STREQUAL "")
        message(
            FATAL_ERROR
            "HSE_EXECUTABLE: Unknown args: ${param_UNPARSED_ARGUMENTS}")
    endif()

    # All make targets that produce executables are suffixed with "-bin".  The
    # target property named "OUTPUT_NAME" defines the name of the executable file.
    add_executable(${param_NAME}-bin ${param_SRCS})
    set_target_properties(${param_NAME}-bin PROPERTIES OUTPUT_NAME ${param_NAME})

    if(NOT "${param_CFLAGS}" STREQUAL "")
        target_compile_options(${param_NAME}-bin PRIVATE ${param_CFLAGS})
    endif()

    if(NOT "${param_OUTDIR}" STREQUAL "")
        set_property(TARGET ${param_NAME}-bin
            PROPERTY RUNTIME_OUTPUT_DIRECTORY ${param_OUTDIR})
    endif()

    if(NOT "${param_DEPS}" STREQUAL "")
        add_dependencies(${param_NAME}-bin ${param_DEPS})
    endif()

    if(NOT "${param_INCLUDES}" STREQUAL "")
        target_include_directories(${param_NAME}-bin PRIVATE ${param_INCLUDES})
    endif()

    # add "-L <link_dir>" for all that aren't already in the flags
    if(NOT "${param_LINK_DIRS}" STREQUAL "")
        get_property(elf VARIABLE PROPERTY CMAKE_EXE_LINKER_FLAGS)
        foreach(link_dir ${param_LINK_DIRS})
            if("${elf}" STREQUAL "")
                set(local_test 1)
            else()
                string(FIND ${elf} "-L ${link_dir}" result)
                if("${result}" STREQUAL "-1")
                    set(local_test 1)
                else()
                    set(local_test 0)
                endif()
            endif()
            if(local_test)
                set(local_linker_flags "${local_linker_flags} -L ${link_dir}")
            endif()
        endforeach(link_dir)
        set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${local_linker_flags}" PARENT_SCOPE)
    endif()

    if(NOT "${param_LINK_LIBS}" STREQUAL "")
        add_dependencies(${param_NAME}-bin ${param_LINK_LIBS})
        target_link_libraries( ${param_NAME}-bin ${param_LINK_LIBS} )
    endif()

    # Do not install artifact unless component has been set
    if(NOT "${param_COMPONENT}" STREQUAL "")
        install(
            TARGETS ${param_NAME}-bin
            DESTINATION ${param_DESTINATION}
            COMPONENT ${param_COMPONENT})
    endif()

endfunction()

#----------------------------------------------------------------
#
# hse_library : Function to build a library
#
#----------------------------------------------------------------
function(hse_library)

    set(one_value_opts
        NAME            # (required) name for collection of object files
	MODE            # (required) one of "SHARED", "STATIC"
        COMPONENT       # (optional) installation component
        OUTPUT_NAME     # (optional) actual name of installed library
        )

    set(multi_value_opts
        SRCS            # (required) list of source or obj files
        DEPS            # CMake targets that must be built first
        CFLAGS          # Additional CFLAGS
        INCLUDES        # Directories to search for #include
        LINK_DIRS       # (optional) List of directories to be searched for libs
        LINK_LIBS       # (optional) Libraries that must be linked along with this lib
        )

    cmake_parse_arguments(
        param
        "" "${one_value_opts}" "${multi_value_opts}"
        ${ARGN})

    if("${param_NAME}" STREQUAL "")
        message(FATAL_ERROR "HSE_LIBRARY: Must set NAME")
    endif()

    if("${param_SRCS}" STREQUAL "")
        message(FATAL_ERROR "HSE_LIBRARY: Must set SRCS")
    endif()

    if(NOT "${param_UNPARSED_ARGUMENTS}" STREQUAL "")
        message(
            FATAL_ERROR
            "HSE_LIBRARY: Unknown args: ${param_UNPARSED_ARGUMENTS}")
    endif()

    if("${param_MODE}" STREQUAL "SHARED")
        add_library(${param_NAME}-lib SHARED ${param_SRCS})
    elseif("${param_MODE}" STREQUAL "STATIC")
        add_library(${param_NAME}-lib STATIC ${param_SRCS})
    else()
        message(FATAL_ERROR "HSE_LIBRARY: Must set MODE to one of SHARED or STATIC")
    endif()

    if("${param_OUTPUT_NAME}" STREQUAL "")
        set_target_properties(${param_NAME}-lib
            PROPERTIES OUTPUT_NAME ${param_NAME})
    else()
        set_target_properties(${param_NAME}-lib
            PROPERTIES OUTPUT_NAME ${param_OUTPUT_NAME})
    endif()

    # for shared libraries add "-L <link_dir>" for all that aren't already in the flags
    if(NOT "${param_LINK_DIRS}" STREQUAL "")
        if("${param_MODE}" STREQUAL "SHARED")
            get_property(sllf VARIABLE PROPERTY CMAKE_SHARED_LINKER_FLAGS)
            foreach(link_dir ${param_LINK_DIRS})
                if("${sllf}" STREQUAL "")
                    set(local_test 1)
                else()
                    string(FIND ${sllf} "-L ${link_dir}" result)
                    if("${result}" STREQUAL "-1")
                        set(local_test 1)
                    else()
                        set(local_test 0)
                    endif()
                endif()
                if(local_test)
                    set(local_linker_flags "${local_linker_flags} -L ${link_dir}")
                endif()
            endforeach(link_dir)
            set(CMAKE_SHARED_LINKER_FLAGS
                "${CMAKE_SHARED_LINKER_FLAGS} ${local_linker_flags}"
                PARENT_SCOPE)
        endif()
    endif()

    target_include_directories(${param_NAME}-lib PRIVATE ${param_INCLUDES})
    target_compile_options(${param_NAME}-lib PRIVATE ${param_CFLAGS})
    target_link_libraries(${param_NAME}-lib ${param_LINK_LIBS})
    if(NOT "${param_DEPS}" STREQUAL "")
        add_dependencies(${param_NAME}-lib ${param_DEPS})
    endif()

    if("${HSE_UNIT_TEST_FLAG}")
        target_compile_options( ${param_NAME}-lib
            PRIVATE "-DHSE_UNIT_TEST_MODE=1" )
    endif()

    # Do not install artifact unless component has been set
    if(NOT "${param_COMPONENT}" STREQUAL "")
        install(
            TARGETS ${param_NAME}-lib
            DESTINATION ${CMAKE_INSTALL_LIBDIR}
            COMPONENT ${param_COMPONENT}
            )
    endif()

endfunction()


#----------------------------------------------------------------
#
# HSE_UT_MOCK_GENERATE : Function to create mock entry point
#                        routines
#
#----------------------------------------------------------------
function(hse_ut_mock_generate)

    if( HSE_UNIT_TEST_FLAG )

        #------------------------------------------------
        # Parse and check args
        #------------------------------------------------
        set(flag_opts
            PRIVATE            # Generate source in include/private
            )

        set(one_value_opts
            GENSRC_PREFIX      # (required) Name governing where the generated
            #    code will land
            )

        set(multi_value_opts
            DEPENDENTS   # Names of the libraries containing ut-mockable entry points
            #    that will be considered
            SEARCH_DIRS  # (required) Directories to search for header files
            #    containing mockable entry points
            )

        cmake_parse_arguments(
            param
            "${flag_opts}" "${one_value_opts}" "${multi_value_opts}"
            ${ARGN})

        if(("${param_DEPENDENTS}" STREQUAL ""))
            message(FATAL_ERROR "HSE_UT_MOCK_GENERATE: Must set dependents")
        endif()

        if("${param_GENSRC_PREFIX}" STREQUAL "")
            message(FATAL_ERROR "HSE_UT_MOCK_GENERATE: Must set GENSRC_PREFIX")
        endif()

        if("${param_SEARCH_DIRS}" STREQUAL "")
            message(FATAL_ERROR "HSE_UT_MOCK_GENERATE: Must set SEARCH_DIRS")
        endif()

        if(NOT "${param_UNPARSED_ARGUMENTS}" STREQUAL "")
            message(
                FATAL_ERROR
                "HSE_UT_MOCK_GENERATE: Unknown args: ${param_UNPARSED_ARGUMENTS}")
        endif()

        string(TOLOWER ${param_GENSRC_PREFIX} gensrc_prefix)

        if(${param_PRIVATE})
            set( output_dir
                ${HSE_STAGE_DIR}/gensrc/${gensrc_prefix}/include/private )
        else()
            set( output_dir
                ${HSE_STAGE_DIR}/gensrc/${gensrc_prefix}/include )
        endif()

        execute_process(
            COMMAND ${HSE_SCRIPTS_DIR}/build/ut_mock_decl_files ${param_SEARCH_DIRS}
            WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
            RESULT_VARIABLE exec_result
            ERROR_VARIABLE find_error
            OUTPUT_VARIABLE ut_mock_file_list
            )

        if( NOT ("${exec_result}" STREQUAL "0" ))
            message( FATAL_ERROR
                "Error searching for mock-aware header files in: ${param_SEARCH_DIRS}" )
        endif()

        if( NOT ("${ut_mock_file_list}" STREQUAL "" ) )
            string(REPLACE "\n" ";" chopped_file_list ${ut_mock_file_list})

            foreach(file ${chopped_file_list})
                my_ut_preproc_file(${file} ${output_dir} result)
                set( UTGEN_FILES ${UTGEN_FILES} ${result} )
            endforeach()

            add_custom_target(
                ${param_GENSRC_PREFIX}_generate_ut
                DEPENDS ${UTGEN_FILES}
                )

            foreach(dependent ${param_DEPENDENTS})
                add_dependencies( ${dependent} ${param_GENSRC_PREFIX}_generate_ut )
            endforeach()

        endif()

    endif()

endfunction()

#----------------------------------------------------------------
#
# HSE_UT_PUBLISH_INCLUDE_DIRS : Function to publish the include
#                               directories containing the
#                               generated header files
#
#----------------------------------------------------------------
function(hse_ut_publish_include_dirs)

    if( HSE_UNIT_TEST_FLAG )

        #------------------------------------------------
        # Parse and check args
        #------------------------------------------------
        set(one_value_opts
            COMPONENT             # (required) Name of the component to publish
            #    include directories for
            )

        cmake_parse_arguments(
            param
            "" "${one_value_opts}" ""
            ${ARGN})

        if("${param_COMPONENT}" STREQUAL "")
            message(FATAL_ERROR "HSE_UT_PUBLISH_INCLUDE_DIRS: Must set COMPONENT")
        endif()

        if(NOT "${param_UNPARSED_ARGUMENTS}" STREQUAL "")
            message(
                FATAL_ERROR
                "HSE_UT_HOUSEKEEPING: Unknown args: ${param_UNPARSED_ARGUMENTS}")
        endif()

        string(TOUPPER ${param_COMPONENT} component_upper)
        string(TOLOWER ${param_COMPONENT} component_lower)

        set( ${component_upper}_UT_GS_INCLUDE_DIRS
            ${HSE_STAGE_DIR}/gensrc/${component_lower}/include
            ${HSE_STAGE_DIR}/gensrc/${component_lower}/include/private
            CACHE
            STRING
            "include directory for component ${param_COMPONENT} ut-generated source"
            )

    endif()

endfunction()

#----------------------------------------------------------------
# hse_doc:  Install documentation with no processing
#
# Usage:
#
#   hse_doc(
#       NAME        foobar
#       SRCS        foo.c bar.c
#       DESTINATION dir
#       COMPONENT   component)
#----------------------------------------------------------------

function(hse_doc)

    set(one_value_opts
        NAME            # (required) name for collection of object files
        COMPONENT       # (required) installation component
        DESTINATION     # (required) installation directory
        )

    set(multi_value_opts
        SRCS            # (required) list of source files
        )

    cmake_parse_arguments(
        param
        "" "${one_value_opts}" "${multi_value_opts}"
        ${ARGN})

    if("${param_NAME}" STREQUAL "")
        message(FATAL_ERROR "HSE_DOC: Must set NAME")
    endif()

    if("${param_SRCS}" STREQUAL "")
        message(FATAL_ERROR "HSE_DOC: Must set SRCS")
    endif()

    if("${param_COMPONENT}" STREQUAL "")
        message(FATAL_ERROR "HSE_DOC: Must set COMPONENT")
    endif()

    if("${param_DESTINATION}" STREQUAL "")
        message(FATAL_ERROR "HSE_DOC: Must set DESTINATION")
    endif()

    # Do not install artifact unless component has been set
    if(NOT "${param_COMPONENT}" STREQUAL "")
        install(
            FILES ${param_SRCS}
            DESTINATION ${param_DESTINATION}
            COMPONENT ${param_COMPONENT}
            )
    endif()

endfunction()

#----------------------------------------------------------------
# hse_mkdocs:  Install documentation with mkdocs processing.
#   Output is an HTML documentation site.
#
# Usage:
#
#   hse_mkdocs(
#       NAME        foobar
#       SRCDIR      foo
#       OPTIONS     mkdocs_opts
#       DESTINATION dir
#       COMPONENT   component)
#----------------------------------------------------------------

function(hse_mkdocs)

    set(one_value_opts
        NAME            # (required) name for document
        SRCDIR          # (required) directory containing docs and YAML cfg
        DESTINATION     # (required) installation directory
        COMPONENT       # (required) installation component
        )

    set(multi_value_opts
        OPTIONS         # (optional) mkdoc options
        )

    cmake_parse_arguments(
        param
        "" "${one_value_opts}" "${multi_value_opts}"
        ${ARGN})

    if("${param_NAME}" STREQUAL "")
        message(FATAL_ERROR "HSE_MKDOCS: Must set NAME")
    endif()

    if("${param_SRCDIR}" STREQUAL "")
        message(FATAL_ERROR "HSE_MKDOCS: Must set SRCDIR")
    endif()

    if("${param_DESTINATION}" STREQUAL "")
        message(FATAL_ERROR "HSE_MKDOCS: Must set DESTINATION")
    endif()

    if("${param_COMPONENT}" STREQUAL "")
        message(FATAL_ERROR "HSE_MKDOCS: Must set COMPONENT")
    endif()

    #
    # Stage out to the build side of the tree.
    #
    file(COPY ${param_SRCDIR} DESTINATION ${CMAKE_CURRENT_BINARY_DIR})

    #
    # Set up a target for the above copy.
    #
    get_filename_component(basename_SRCDIR ${param_SRCDIR} NAME)
    set(BUILD_SRCDIR "${CMAKE_CURRENT_BINARY_DIR}/${basename_SRCDIR}")

    add_custom_target(
        ${param_NAME}-srcs
        ALL
        DEPENDS ${BUILD_SRCDIR}
        )

    #
    # Generate the website.  The outputs are not completely
    # deterministic, so we'll depend upon the top index.html and hope
    # for the rest as a side-effect.
    #
    file(GLOB mdsrcs ${param_SRCDIR}/docs/*.md)
    list(APPEND mdsrcs ${param_SRCDIR}/mkdocs.yml)

    #
    # RHEL vs. FC handles locales differently (of course).
    #
    if( "${HSE_DISTRO}" STREQUAL "el6" OR "${HSE_DISTRO}" STREQUAL "el7" )
        set(HSE_LOCALE "en_US.utf-8")
    else()
        set(HSE_LOCALE "en_US.UTF-8")
    endif()

    add_custom_command(
        OUTPUT "${BUILD_SRCDIR}/site/index.html"
        COMMAND env LC_ALL=${HSE_LOCALE} LANG=${HSE_LOCALE} mkdocs build ${param_OPTIONS}
        DEPENDS ${mdsrcs}
        WORKING_DIRECTORY ${BUILD_SRCDIR}
        )

    add_custom_target(
        ${param_NAME}-site
        ALL
        DEPENDS "${BUILD_SRCDIR}/site/index.html"
        )

    # Do not install artifact unless component has been set
    if(NOT "${param_COMPONENT}" STREQUAL "")
        install(
            DIRECTORY "${BUILD_SRCDIR}/site/"
            DESTINATION ${param_DESTINATION}
            COMPONENT ${param_COMPONENT})
    endif()

endfunction()

#----------------------------------------------------------------
# hse_pandoc:  Install documentation with pandoc processing.
#   Output is HTML and PDF as standalone documents.
#
# Usage:
#
#   hse_pandoc(
#       NAME        foobar
#       SRCS        foo.md
#       STYLESHEET  bar.css
#       OPTIONS     pandoc_opts
#       DESTINATION dir
#       COMPONENT   component)
#----------------------------------------------------------------

function(hse_pandoc)

    set(one_value_opts
        NAME            # (required) name for document
        STYLESHEET      # (optional) stylesheet for HTML output
        OPTIONS         # (optional) pandoc options to override defaults
        DESTINATION     # (required) installation directory
        COMPONENT       # (required) installation component
        )

    set(multi_value_opts
        SRCS            # (required) list of source files
        )

    set(pandoc_common_opts  "--toc" "--smart" "-s")
    set(pandoc_pdf_opts     "-V" "geometry:margin=1in")

    set(pandoc_pdf_opts     "${pandoc_pdf_opts}" "${pandoc_common_opts}")

    cmake_parse_arguments(
        param
        "" "${one_value_opts}" "${multi_value_opts}"
        ${ARGN})

    if("${param_NAME}" STREQUAL "")
        message(FATAL_ERROR "HSE_PANDOC: Must set NAME")
    endif()

    if("${param_SRCS}" STREQUAL "")
        message(FATAL_ERROR "HSE_PANDOC: Must set SRCS")
    endif()

    if(NOT "${param_OPTIONS}" STREQUAL "")
        set(pandoc_html_opts "${param_OPTIONS}")
        set(pandoc_pdf_opts  "${param_OPTIONS}")
    endif()

    if(NOT "${param_STYLESHEET}" STREQUAL "")
        set(
          pandoc_html_opts "${pandoc_html_opts}" "--css" "${param_STYLESHEET}")
        set(
          pandoc_html_opts "${pandoc_html_opts}" "${pandoc_common_opts}")
    endif()

    if("${param_DESTINATION}" STREQUAL "")
        message(FATAL_ERROR "HSE_PANDOC: Must set DESTINATION")
    endif()

    if("${param_COMPONENT}" STREQUAL "")
        message(FATAL_ERROR "HSE_PANDOC: Must set COMPONENT")
    endif()

    set(
      pdf_output_file
      "${CMAKE_CURRENT_BINARY_DIR}/${param_NAME}.pdf"
    )

    set(
      html_output_file
      "${CMAKE_CURRENT_BINARY_DIR}/${param_NAME}.html"
    )

    my_copy_files(
      ${param_SRCS}-src
      ${CMAKE_CURRENT_BINARY_DIR}
      ${param_SRCS}
    )

    add_custom_command(
        OUTPUT ${pdf_output_file} ${html_output_file}
        COMMAND pandoc ${pandoc_pdf_opts} -o ${pdf_output_file} ${param_SRCS}
        COMMAND pandoc ${pandoc_html_opts} -o ${html_output_file} ${param_SRCS}
        DEPENDS ${param_SRCS} ${param_STYLESHEET} ${param_SRCS}-src
    )

    add_custom_target(
        ${param_NAME}-doc
        ALL
        DEPENDS ${pdf_output_file} ${html_output_file}
    )

    # Do not install artifact unless component has been set
    if(NOT "${param_COMPONENT}" STREQUAL "")
        install(
            FILES ${pdf_output_file} ${html_output_file}
            DESTINATION ${param_DESTINATION}
            COMPONENT ${param_COMPONENT})
    endif()

endfunction()