Revision: 77587
http://sourceforge.net/p/brlcad/code/77587
Author: starseeker
Date: 2020-10-23 02:53:30 +0000 (Fri, 23 Oct 2020)
Log Message:
-----------
Add the custom external project logic at the core of the new 3rd party building
process.
Added Paths:
-----------
brlcad/branches/extbuild/src/other/ext/CMake/ExternalProject_Target.cmake
Added: brlcad/branches/extbuild/src/other/ext/CMake/ExternalProject_Target.cmake
===================================================================
--- brlcad/branches/extbuild/src/other/ext/CMake/ExternalProject_Target.cmake
(rev 0)
+++ brlcad/branches/extbuild/src/other/ext/CMake/ExternalProject_Target.cmake
2020-10-23 02:53:30 UTC (rev 77587)
@@ -0,0 +1,439 @@
+# This is based on the logic generated by CMake for EXPORT, but customized for
+# use with ExternalProject:
+#
+# https://cmake.org/cmake/help/latest/module/ExternalProject.html
+#
+# The goal is to create an imported target based on the ExternalProject
+# information, and then append the necessary install logic to manage RPath
+# settings in the external projects as if the external files were built by the
+# main CMake project.
+
+# The catch to this is that the external project outputs MUST be built in a way
+# that is compatible with CMake's RPath handling assumptions. See
+#
https://stackoverflow.com/questions/41175354/can-i-install-shared-imported-library
+# for one of the issues surrounding this - file(RPATH_CHANGE) must be able to
+# succeed, and it is up to the 3rd party build setups to prepare their outputs
+# to be ready. The key variable CMAKE_BUILD_RPATH must be set correctly ahead
+# of time - see RPath_Setup.cmake
+
+# Be quite about tool outputs by default
+if(NOT DEFINED EXTPROJ_VERBOSE)
+ set(EXTPROJ_VERBOSE 0)
+endif(NOT DEFINED EXTPROJ_VERBOSE)
+
+# cmake -E copy follows symlinks to get the final file, which is not what we
+# want in this situation. To avoid this, we create a copy script which uses
+# file(COPY) and run that script with cmake -P
+file(WRITE "${CMAKE_BINARY_DIR}/CMakeFiles/cp.cmake"
"get_filename_component(DDIR \${DEST} DIRECTORY)\nfile(COPY \${SRC} DESTINATION
\${DDIR})")
+
+# When staging files in the build directory, we have to be aware of multiple
+# configurations. This is done post-ExternalProject build, at the parent build
+# time, so it needs to be a custom command. Until add_custom_command outputs
+# can use $<CONFIG>, we use the old-school CMAKE_CFG_INTDIR variable in the
OUTPUT
+# path. This is key, because it allows the add_custom_command to be aware that
+# it needs to execute the correct copy command for a current configuration at
build
+# time.
+function(fcfgcpy outvar extproj root ofile dir tfile)
+ string(REPLACE "${CMAKE_BINARY_DIR}/" "" rdir "${dir}")
+ add_custom_command(
+ OUTPUT "${CMAKE_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${rdir}/${tfile}"
+ COMMAND ${CMAKE_COMMAND} -DSRC="${root}/${rdir}/${ofile}"
-DDEST="${CMAKE_BINARY_DIR}/$<CONFIG>/${rdir}/${tfile}" -P
"${CMAKE_BINARY_DIR}/CMakeFiles/cp.cmake"
+ DEPENDS ${extproj}
+ )
+ set(TOUT ${TOUT} "${CMAKE_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${rdir}/${tfile}")
+ set(${outvar} ${TOUT} PARENT_SCOPE)
+endfunction(fcfgcpy file)
+
+# Custom patch utility to replace the build directory path with the install
+# directory path in text files - make sure CMAKE_BINARY_DIR and
+# CMAKE_INSTALL_PREFIX are finalized before generating this file!
+configure_file(${${CMAKE_PROJECT_NAME}_CMAKE_DIR}/buildpath_replace.cxx.in
${CMAKE_CURRENT_BINARY_DIR}/buildpath_replace.cxx)
+add_executable(buildpath_replace
${CMAKE_CURRENT_BINARY_DIR}/buildpath_replace.cxx)
+
+function(ExternalProject_ByProducts etarg extproj extroot dir)
+
+ cmake_parse_arguments(E "FIXPATH;NOINSTALL" "" "" ${ARGN})
+
+ if (EXTPROJ_VERBOSE)
+
+ list(LENGTH E_UNPARSED_ARGUMENTS FCNT)
+ if (E_FIXPATH)
+ if (FCNT GREATER 1)
+ message("${extproj}: Adding path adjustment and installation rules for
${FCNT} byproducts")
+ else (FCNT GREATER 1)
+ message("${extproj}: Adding path adjustment and installation rules for
${FCNT} byproduct")
+ endif (FCNT GREATER 1)
+ else (E_FIXPATH)
+ if (FCNT GREATER 1)
+ message("${extproj}: Adding install rules for ${FCNT} byproducts")
+ else (FCNT GREATER 1)
+ message("${extproj}: Adding install rules for ${FCNT} byproduct")
+ endif (FCNT GREATER 1)
+ endif (E_FIXPATH)
+
+ endif (EXTPROJ_VERBOSE)
+
+ if (NOT TARGET ${etarg}_stage)
+ add_custom_target(${etarg}_stage ALL)
+ endif (NOT TARGET ${etarg}_stage)
+
+ set(ALL_TOUT)
+ foreach (bpf ${E_UNPARSED_ARGUMENTS})
+
+ unset(TOUT)
+ fcfgcpy(TOUT ${extproj} ${extroot} ${bpf} "${dir}" ${bpf})
+ set(ALL_TOUT ${ALL_TOUT} ${TOUT})
+
+ if (NOT E_NOINSTALL)
+ install(FILES "${CMAKE_BINARY_ROOT}/${dir}/${bpf}" DESTINATION "${dir}/")
+ if (E_FIXPATH)
+ # Note - proper quoting for install(CODE) is extremely important for
CPack, see
+ # https://stackoverflow.com/a/48487133
+ install(CODE "execute_process(COMMAND $<TARGET_FILE:buildpath_replace>
\"\${CMAKE_INSTALL_PREFIX}/${dir}/${bpf}\")")
+ endif (E_FIXPATH)
+ endif (NOT E_NOINSTALL)
+
+ endforeach (bpf ${E_UNPARSED_ARGUMENTS})
+
+ if (ALL_TOUT)
+ string(MD5 UKEY "${ALL_TOUT}")
+ add_custom_target(${etarg}_${UKEY} ALL DEPENDS ${ALL_TOUT})
+ add_dependencies(${etarg}_stage ${etarg}_${UKEY})
+ endif (ALL_TOUT)
+
+endfunction(ExternalProject_ByProducts)
+
+
+function(ET_target_props etarg REL_DIR LINK_TARGET)
+
+ cmake_parse_arguments(ET "SHARED" "LINK_TARGET_DEBUG" "" ${ARGN})
+
+ if(NOT CMAKE_CONFIGURATION_TYPES)
+
+ # Note: per https://stackoverflow.com/a/49390802 need to set
+ # IMPORTED_NO_SONAME to get working linking for what we're trying to do
+ # here. Without that property set, build dir copies of libraries will use
+ # an incorrect relative link and fail to find the library.
+ set_property(TARGET ${etarg} APPEND PROPERTY IMPORTED_CONFIGURATIONS
NOCONFIG)
+ set_target_properties(${etarg} PROPERTIES
+ IMPORTED_NO_SONAME TRUE
+ IMPORTED_LOCATION_NOCONFIG
"${CMAKE_BINARY_DIR}/${REL_DIR}/${LINK_TARGET}"
+ IMPORTED_SONAME_NOCONFIG "${LINK_TARGET}"
+ )
+
+ # For Windows, IMPORTED_IMPLIB is important for shared libraries.
+ # It is that property that will tell a toplevel target what to link against
+ # when building - pointing out the dll isn't enough by itself.
+ if(ET_SHARED AND MSVC)
+ string(REPLACE "${CMAKE_SHARED_LIBRARY_SUFFIX}" ".lib" IMPLIB_FILE
"${LINK_TARGET}")
+ string(REPLACE "${SHARED_DIR}" "${LIB_DIR}" REL_DIR "${REL_DIR}")
+ set_target_properties(${etarg} PROPERTIES
+ IMPORTED_IMPLIB_NOCONFIG "${CMAKE_BINARY_DIR}/${REL_DIR}/${IMPLIB_FILE}"
+ )
+ endif(ET_SHARED AND MSVC)
+
+ else(NOT CMAKE_CONFIGURATION_TYPES)
+
+ # If no config is set for multiconfig, default to Debug
+ set_target_properties(${etarg} PROPERTIES
+ IMPORTED_NO_SONAME TRUE
+ IMPORTED_LOCATION "${CMAKE_BINARY_DIR}/Debug/${REL_DIR}/${LINK_TARGET}"
+ IMPORTED_SONAME "${LINK_TARGET}"
+ )
+
+ foreach(CFG_TYPE ${CMAKE_CONFIGURATION_TYPES})
+ string(TOUPPER "${CFG_TYPE}" CFG_TYPE_UPPER)
+
+ # The config variables need to be set in this mode.
+ if("${CFG_TYPE_UPPER}" STREQUAL "DEBUG" AND ET_LINK_TARGET_DEBUG)
+ set(C_LINK_TARGET ${ET_LINK_TARGET_DEBUG})
+ else()
+ set(C_LINK_TARGET ${LINK_TARGET})
+ endif()
+
+ # If we're multiconfig, define properties for each configuration
+ set_target_properties(${etarg} PROPERTIES
+ IMPORTED_CONFIGURATIONS "${CMAKE_CONFIGURATION_TYPES}"
+ IMPORTED_NO_SONAME_${CFG_TYPE_UPPER} TRUE
+ IMPORTED_LOCATION_${CFG_TYPE_UPPER}
"${CMAKE_BINARY_DIR}/${CFG_TYPE}/${REL_DIR}/${LINK_TARGET}"
+ IMPORTED_SONAME_${CFG_TYPE_UPPER} "${LINK_TARGET}"
+ )
+
+ # For Windows, IMPORTED_IMPLIB is important for shared libraries.
+ # It is that property that will tell a toplevel target what to link
against
+ # when building - pointing out the dll isn't enough by itself.
+ if(ET_SHARED AND MSVC)
+ string(REPLACE "${CMAKE_SHARED_LIBRARY_SUFFIX}" ".lib" IMPLIB_FILE
"${LINK_TARGET}")
+ string(REPLACE "${SHARED_DIR}" "${LIB_DIR}" REL_DIR "${REL_DIR}")
+ set_target_properties(${etarg} PROPERTIES
+ IMPORTED_IMPLIB_${CFG_TYPE_UPPER}
"${CMAKE_BINARY_DIR}/${CFG_TYPE}/${REL_DIR}/${IMPLIB_FILE}"
+ )
+ endif(ET_SHARED AND MSVC)
+
+ endforeach(CFG_TYPE ${CMAKE_CONFIGURATION_TYPES})
+
+ endif(NOT CMAKE_CONFIGURATION_TYPES)
+
+endfunction(ET_target_props)
+
+
+# For a given path, calculate the $ORIGIN style path needed relative
+# to CMAKE_INSTALL_PREFIX
+function(ET_Origin_Path POPATH INIT_PATH)
+
+ get_filename_component(CPATH "${INIT_PATH}" REALPATH)
+ set(RELDIRS)
+ set(FPATH)
+ while (NOT "${CPATH}" STREQUAL "${CMAKE_INSTALL_PREFIX}")
+ get_filename_component(CDIR "${CPATH}" NAME)
+ get_filename_component(CPATH "${CPATH}" DIRECTORY)
+ if (NOT "${RELDIRS}" STREQUAL "")
+ set(RELDIRS "${CDIR}/${RELDIRS}")
+ set(FPATH "../${FPATH}")
+ else (NOT "${RELDIRS}" STREQUAL "")
+ set(RELDIRS "${CDIR}")
+ set(FPATH "../")
+ endif (NOT "${RELDIRS}" STREQUAL "")
+ endwhile()
+
+ set(FPATH "${FPATH}${RELDIRS}")
+
+ set(${POPATH} ${FPATH} PARENT_SCOPE)
+endfunction(ET_Origin_Path)
+
+# Mimic the magic of the CMake install(TARGETS) form of the install command.
+# This is the key to treating external project build outputs as fully managed
+# CMake outputs, and requires that the external project build in such a way
+# that the rpath settings in the build outputs are compatible with this
+# mechanism.
+#
+# When specifying OFILE, the path should be given relative to the root
directory:
+# For example:
+#
+# /usr/lib/libfoo.so -> lib/libfoo.so
+# /usr/lib/bar-1.0/libbar.so -> lib/foo-1.0/libfoo.so
+# /usr/bin/baz -> bin/baz
+# /usr/bin/mypkg/baz -> bin/mypkg/baz
+#
+function(ET_RPath OFILE)
+ get_filename_component(OFPATH "${OFILE}" DIRECTORY)
+ get_filename_component(RRPATH "${CMAKE_INSTALL_PREFIX}/${OFPATH}" REALPATH)
+ set(OPATH)
+ ET_Origin_Path(OPATH "${RRPATH}")
+ if (NOT APPLE)
+ set(NEW_RPATH "$ENV{DESTDIR}${RRPATH}:$ORIGIN/${OPATH}")
+ else (NOT APPLE)
+ set(NEW_RPATH "$ENV{DESTDIR}${RRPATH}:@loader_path/${OPATH}")
+ endif (NOT APPLE)
+ if (NOT DEFINED CMAKE_BUILD_RPATH)
+ message(FATAL_ERROR "ET_RPath run without CMAKE_BUILD_RPATH defined - run
cmake_set_rpath before defining external projects.")
+ endif (NOT DEFINED CMAKE_BUILD_RPATH)
+ # Note - proper quoting for install(CODE) is extremely important for CPack,
see
+ # https://stackoverflow.com/a/48487133
+ install(CODE "
+ file(RPATH_CHANGE
+ FILE \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${OFILE}\"
+ OLD_RPATH \"${CMAKE_BUILD_RPATH}\"
+ NEW_RPATH \"${NEW_RPATH}\")
+ ")
+endfunction(ET_RPath)
+
+
+# etype = EXEC;SHARED;STATIC
+#
+function(ExternalProject_Target etype etarg extproj extroot fname)
+
+ cmake_parse_arguments(E "RPATH" "LINK_TARGET;LINK_TARGET_DEBUG;SUBDIR"
"SYMLINKS" ${ARGN})
+
+ #message("etype: ${etype}")
+ #message("etarg: ${etarg}")
+ #message("extproj: ${extproj}")
+ #message("extroot: ${extroot}")
+ #message("fname: ${fname}")
+
+ # If we have a static target but BUILD_STATIC_LIBS is off, we're done
+ if ("${etype}" STREQUAL "STATIC" AND NOT BUILD_STATIC_LIBS)
+ return()
+ endif ("${etype}" STREQUAL "STATIC" AND NOT BUILD_STATIC_LIBS)
+
+ # If we have something that's not a target, fatal error - this is a problem
with the build logic
+ if(NOT TARGET ${extproj})
+ message(FATAL_ERROR "${extprog} is not a target")
+ endif(NOT TARGET ${extproj})
+
+ # Protect against redefinition of already defined targets - each etarg name
should be unique, corresponding
+ # to a single executable or library from the foreign build.
+ if(TARGET ${etarg})
+ message(FATAL_ERROR "Target ${etarg} is already defined\n")
+ endif(TARGET ${etarg})
+
+ # For some platforms, we may want to target a symbolic link rather than the
+ # target itself for linking purposes. We also (grr) may need to target a
+ # configuration specific link. Go through the permutations and figure out
+ # what LINK_TARGET should be in this case.
+ unset(LINK_TARGET)
+ unset(LINK_TARGET_DEBUG)
+
+ if ("${etype}" STREQUAL "SHARED")
+
+ if (E_LINK_TARGET)
+ set(LINK_TARGET "${E_LINK_TARGET}")
+ endif (E_LINK_TARGET)
+ if (NOT LINK_TARGET)
+ # In case we have a relative path for fname, make sure we're using just
+ # the filename for linking purposes.
+ get_filename_component(LFILE "${fname}" NAME)
+ set(LINK_TARGET "${LFILE}")
+ endif (NOT LINK_TARGET)
+
+ if (E_LINK_TARGET_DEBUG)
+ set(LINK_TARGET_DEBUG "${E_LINK_TARGET_DEBUG}")
+ else (LINK_TARGET)
+ set(LINK_TARGET_DEBUG "${LINK_TARGET}")
+ endif ()
+
+ endif ("${etype}" STREQUAL "SHARED")
+
+ # Create imported target - need to both make the target itself
+ # and set the necessary properties. See also
+ #
https://gitlab.kitware.com/cmake/community/wikis/doc/tutorials/Exporting-and-Importing-Targets
+
+ # Because the outputs are not properly build target outputs of the primary
+ # CMake project, we need to install as either FILES or PROGRAMS. Collect
+ # the file copying targets in a variable for later use.
+ set(TOUT)
+
+ # Shared library logic
+ if ("${etype}" STREQUAL "SHARED")
+
+ add_library(${etarg} SHARED IMPORTED GLOBAL)
+
+ if (MSVC)
+ set(SHARED_DIR ${BIN_DIR})
+ else (MSVC)
+ set(SHARED_DIR ${LIB_DIR})
+ endif (MSVC)
+
+ fcfgcpy(TOUT ${extproj} ${extroot} ${fname} ${SHARED_DIR} ${fname})
+
+ # On Windows, we need both a .dll and a .lib file to use a shared library
for compilation
+ if (MSVC)
+ string(REPLACE "${CMAKE_SHARED_LIBRARY_SUFFIX}" ".lib" IMPLIB_FILE
"${fname}")
+ fcfgcpy(TOUT ${extproj} ${extroot} ${IMPLIB_FILE} ${LIB_DIR}
${IMPLIB_FILE})
+ endif (MSVC)
+
+ # Because we're using LINK_TARGET here rather than fname, we need to take
any relative
+ # directories specified in fname and ppend them to SHARED_DIR. (For other
cases we are
+ # just feeding fname directly, so there are no such concerns. TODO -
should we just
+ # require relative dirs on all the symlinks instead?
+ get_filename_component(LDIR "${fname}" DIRECTORY)
+ ET_target_props(${etarg} "${SHARED_DIR}/${LDIR}" ${LINK_TARGET} SHARED
LINK_TARGET_DEBUG "${LINK_TARGET_DEBUG}")
+
+ install(FILES "${CMAKE_BINARY_ROOT}/${SHARED_DIR}/${fname}" DESTINATION
${SHARED_DIR}/${E_SUBDIR})
+ if (MSVC)
+ install(FILES "${CMAKE_BINARY_ROOT}/${LIB_DIR}/${IMPLIB_FILE}"
DESTINATION ${LIB_DIR}/${E_SUBDIR})
+ endif (MSVC)
+
+ # Let CMake know there is a target dependency here, despite this being an
import target
+ add_dependencies(${etarg} ${extproj})
+
+ # additional (non-MSVC) work specific to shared libraries:
+ if (NOT MSVC)
+
+ # Perform RPath magic
+ if (E_RPATH)
+ ET_RPath("${SHARED_DIR}/${fname}")
+ endif (E_RPATH)
+
+ # Add install rules for any symlinks the caller has listed
+ foreach(slink ${E_SYMLINKS})
+ fcfgcpy(TOUT ${extproj} ${extroot} ${slink} ${SHARED_DIR} ${slink})
+ install(FILES "${CMAKE_BINARY_ROOT}/${SHARED_DIR}/${slink}" DESTINATION
${SHARED_DIR}/${E_SUBDIR})
+ endforeach(slink ${E_SYMLINKS})
+
+ endif (NOT MSVC)
+
+ endif ("${etype}" STREQUAL "SHARED")
+
+ # Static library logic
+ if ("${etype}" STREQUAL "STATIC" AND BUILD_STATIC_LIBS)
+
+ add_library(${etarg} STATIC IMPORTED GLOBAL)
+
+ fcfgcpy(TOUT ${extproj} ${extroot} ${fname} ${LIB_DIR} ${fname})
+
+ ET_target_props(${etarg} "${LIB_DIR}" ${fname} STATIC LINK_TARGET_DEBUG
"${LINK_TARGET_DEBUG}")
+ install(FILES "${CMAKE_BINARY_ROOT}/${LIB_DIR}/${fname}" DESTINATION
${LIB_DIR}/${E_SUBDIR})
+
+ # Let CMake know there is a target dependency here, despite this being an
import target
+ add_dependencies(${etarg} ${extproj})
+
+ # additional (non-MSVC) work specific to shared libraries:
+ if (NOT MSVC)
+
+ # Add install rules for any symlinks the caller has listed
+ foreach(slink ${E_SYMLINKS})
+ fcfgcpy(TOUT ${extproj} ${extroot} ${slink} ${LIB_DIR} ${slink})
+ install(FILES "${CMAKE_BINARY_ROOT}/${LIB_DIR}/${slink}" DESTINATION
${LIB_DIR})
+ endforeach(slink ${E_SYMLINKS})
+
+ endif (NOT MSVC)
+
+ endif ("${etype}" STREQUAL "STATIC" AND BUILD_STATIC_LIBS)
+
+ # Executable logic
+ if ("${etype}" STREQUAL "EXEC")
+
+ add_executable(${etarg} IMPORTED GLOBAL)
+
+ fcfgcpy(TOUT ${extproj} ${extroot} ${fname} ${BIN_DIR} ${fname})
+
+ ET_target_props(${etarg} "${BIN_DIR}" ${fname})
+
+ install(PROGRAMS "${CMAKE_BINARY_ROOT}/${BIN_DIR}/${fname}" DESTINATION
${BIN_DIR}/${E_SUBDIR})
+
+ # Let CMake know there is a target dependency here, despite this being an
import target
+ add_dependencies(${etarg} ${extproj})
+
+ # additional (non-MSVC) work specific to shared libraries:
+ if (NOT MSVC)
+
+ # Perform RPath magic
+ if (E_RPATH)
+ ET_RPath("${BIN_DIR}/${fname}")
+ endif (E_RPATH)
+
+ endif (NOT MSVC)
+
+ endif ("${etype}" STREQUAL "EXEC")
+
+ # Set up the staging targets - these copy the files we are interested in
from the
+ # 3rd party install directory to BRL-CAD's build tree. This allows us to
achieve
+ # file level dependencies despite using ExternalProject_Add, since each
output
+ # file uses a custom command to do the copy that depends on the external
target
+ # and the staging target depends on all of those output files individually.
In
+ # essence, those copy commands provide our CMake system with the explicit
file
+ # information we could not otherwise get from ExternalProject_Add.
+ if (TOUT)
+ if (NOT TARGET ${etarg}_stage)
+ add_custom_target(${etarg}_stage ALL DEPENDS ${TOUT})
+ else (NOT TARGET ${etarg}_stage)
+ add_dependencies(${etarg}_stage ${TOUT})
+ endif (NOT TARGET ${etarg}_stage)
+ endif (TOUT)
+
+ # Set up dependencies for the imported target that will be used in *_LIBRARY
variables
+ # to depend on the staging targets. This completes the dependency chain,
and targets
+ # depending on the *_LIBRARY variables will now depend on the correct
execution of
+ # the ExternalProject_Add system.
+ add_dependencies(${etarg} ${etarg}_stage)
+
+endfunction(ExternalProject_Target)
+
+# Local Variables:
+# tab-width: 8
+# mode: cmake
+# indent-tabs-mode: t
+# End:
+# ex: shiftwidth=2 tabstop=8
+
Property changes on:
brlcad/branches/extbuild/src/other/ext/CMake/ExternalProject_Target.cmake
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
This was sent by the SourceForge.net collaborative development platform, the
world's largest Open Source development site.
_______________________________________________
BRL-CAD Source Commits mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/brlcad-commits