| # Ceres Solver - A fast non-linear least squares minimizer |
| # Copyright 2019 Google Inc. All rights reserved. |
| # http://ceres-solver.org/ |
| # |
| # Redistribution and use in source and binary forms, with or without |
| # modification, are permitted provided that the following conditions are met: |
| # |
| # * Redistributions of source code must retain the above copyright notice, |
| # this list of conditions and the following disclaimer. |
| # * Redistributions in binary form must reproduce the above copyright notice, |
| # this list of conditions and the following disclaimer in the documentation |
| # and/or other materials provided with the distribution. |
| # * Neither the name of Google Inc. nor the names of its contributors may be |
| # used to endorse or promote products derived from this software without |
| # specific prior written permission. |
| # |
| # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
| # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| # POSSIBILITY OF SUCH DAMAGE. |
| # |
| # Author: darius.rueckert@fau.de (Darius Rueckert) |
| |
| # The directory containing the following files |
| # - codegen_include.h.in |
| # - generate_code_from_functor.cc.in |
| set(CODEGEN_CMAKE_SCRIPT_DIR "${CMAKE_CURRENT_LIST_DIR}") |
| |
| # Generates C-code implementation of Ceres' CostFunction::Evaluate() API from a |
| # templated C++ cost functor derived from ceres::CodegenCostFunction using |
| # autodiff. The resulting implementation replaces the direct instantiation of |
| # autodiff in client code, typically resulting in improved performance. |
| # |
| # Parameters: |
| # |
| # NAME |
| # The name of the cost functor. The name must exactly match the C++ type |
| # name of the functor. This is also the name of the CMake output target. |
| # NAMESPACE [optional] |
| # The C++ namespace of the cost functor type. For example, if the full |
| # type name is ceres::BundleAdjust, then NAME should be "BundleAdjust" |
| # and NAMESPACE should be "ceres". |
| # INPUT_FILE |
| # The path to the header defining the cost functor <NAME>. |
| # OUTPUT_DIRECTORY [default = "generated"] |
| # The relative output directory of the generated header file. This is the |
| # prefix that has to be added to the #include of the generated files, i.e: |
| # #include "<OUTPUT_DIRECTORY>/<GENERATED_FILE.h>" |
| |
| # |
| # Example Usage: |
| # ceres_generate_cost_function_implementation_for_functor( |
| # NAME SquareFunctor |
| # INPUT_FILE ${CMAKE_CURRENT_SOURCE_DIR}/square_functor.h |
| # ) |
| # add_executable(helloworld_codegen helloworld_codegen.cc ) |
| # target_link_libraries(helloworld_codegen ceres SquareFunctor) |
| function(ceres_generate_cost_function_implementation_for_functor) |
| # Define and parse arguments |
| set(OPTIONAL_ARGS) |
| set(ONE_VALUE_ARGS NAME INPUT_FILE OUTPUT_DIRECTORY NAMESPACE) |
| set(MULTI_VALUE_ARGS) |
| cmake_parse_arguments( |
| COST_FUNCTOR "${OPTIONAL_ARGS}" "${ONE_VALUE_ARGS}" |
| "${MULTI_VALUE_ARGS}" ${ARGN} ) |
| |
| # Default value of the output directory |
| set(OUTPUT_DIRECTORY "generated") |
| if(COST_FUNCTOR_OUTPUT_DIRECTORY) |
| set(OUTPUT_DIRECTORY "${COST_FUNCTOR_OUTPUT_DIRECTORY}") |
| endif() |
| |
| set(CALLER_CODEGEN_BUILD_DIR "${PROJECT_BINARY_DIR}/codegen") |
| set(CALLER_CODEGEN_INCLUDE_DIR "${CALLER_CODEGEN_BUILD_DIR}/include/") |
| |
| file(MAKE_DIRECTORY |
| "${CALLER_CODEGEN_BUILD_DIR}") |
| file(MAKE_DIRECTORY |
| "${CALLER_CODEGEN_INCLUDE_DIR}/${OUTPUT_DIRECTORY}") |
| file(MAKE_DIRECTORY |
| "${CALLER_CODEGEN_BUILD_DIR}/src") |
| |
| # Convert the input file to an absolute path and check if it exists |
| get_filename_component( |
| COST_FUNCTOR_INPUT_FILE "${COST_FUNCTOR_INPUT_FILE}" REALPATH) |
| if(NOT EXISTS "${COST_FUNCTOR_INPUT_FILE}") |
| message(FATAL_ERROR |
| "Could not find codegen input file ${COST_FUNCTOR_INPUT_FILE}") |
| endif() |
| |
| |
| # The full C++ type name of the cost functor. This is used inside the |
| # generator to create an object of it. |
| set(FULL_CXX_FUNCTOR_TYPE_NAME "${COST_FUNCTOR_NAME}") |
| if(COST_FUNCTOR_NAMESPACE) |
| set(FULL_CXX_FUNCTOR_TYPE_NAME |
| "${COST_FUNCTOR_NAMESPACE}::${FULL_CXX_FUNCTOR_TYPE_NAME}") |
| endif() |
| |
| # 1. Generate a wrapper include file which is included by the user. |
| # This is required, because |
| # - It must exist during compiliation of the code generator (otherwise |
| # the #include will fail) |
| # - We don't want to have the users add macros to their code |
| string(TOLOWER "${COST_FUNCTOR_NAME}" LOWER_CASE_FUNCTOR_NAME) |
| set(INCLUDE_FILE |
| "${CALLER_CODEGEN_INCLUDE_DIR}/${OUTPUT_DIRECTORY}/${LOWER_CASE_FUNCTOR_NAME}.h") |
| configure_file("${CODEGEN_CMAKE_SCRIPT_DIR}/codegen_include.inc.in" "${INCLUDE_FILE}") |
| |
| # 2. Generate the source file for the code generator |
| set(GENERATOR_SOURCE |
| "${CALLER_CODEGEN_BUILD_DIR}/src/${LOWER_CASE_FUNCTOR_NAME}_code_generator.cc") |
| set(GENERATED_EVALUATION_IMPL_FILE |
| "${CALLER_CODEGEN_INCLUDE_DIR}/${OUTPUT_DIRECTORY}/${LOWER_CASE_FUNCTOR_NAME}_evaluate_impl.inc") |
| configure_file( |
| "${CODEGEN_CMAKE_SCRIPT_DIR}/generate_code_for_functor.cc.in" "${GENERATOR_SOURCE}") |
| |
| # 3. Build the executable that generates the autodiff code |
| set(GENERATOR_TARGET ${COST_FUNCTOR_NAME}_generator) |
| add_executable(${GENERATOR_TARGET} "${GENERATOR_SOURCE}") |
| target_link_libraries(${GENERATOR_TARGET} ceres) |
| set_target_properties(${GENERATOR_TARGET} PROPERTIES |
| RUNTIME_OUTPUT_DIRECTORY "${CALLER_CODEGEN_BUILD_DIR}/bin") |
| target_compile_definitions(${GENERATOR_TARGET} PRIVATE -DCERES_CODEGEN) |
| target_include_directories(${GENERATOR_TARGET} |
| PRIVATE "${CALLER_CODEGEN_INCLUDE_DIR}") |
| |
| # 4. Execute the program from (3.) using a custom command |
| add_custom_command(OUTPUT "${GENERATED_EVALUATION_IMPL_FILE}" |
| COMMAND ${GENERATOR_TARGET} |
| DEPENDS "${COST_FUNCTOR_INPUT_FILE}" |
| VERBATIM |
| ) |
| set(GENERATE_TARGET ${COST_FUNCTOR_NAME}_generate) |
| add_custom_target(${GENERATE_TARGET} DEPENDS "${GENERATED_EVALUATION_IMPL_FILE}" VERBATIM) |
| |
| # 5. Create an output target which can be used by the client. This is required, |
| # because custom targets can't have include directories. |
| set(OUTPUT_TARGET ${COST_FUNCTOR_NAME}) |
| add_library(${OUTPUT_TARGET} INTERFACE) |
| target_include_directories( |
| ${OUTPUT_TARGET} INTERFACE "${CALLER_CODEGEN_INCLUDE_DIR}") |
| target_sources( |
| ${OUTPUT_TARGET} INTERFACE "${INCLUDE_FILE}" "${GENERATED_EVALUATION_IMPL_FILE}") |
| add_dependencies(${OUTPUT_TARGET} ${GENERATE_TARGET}) |
| endfunction() |