Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add individual targets for Python commands #3069

Open
wants to merge 2 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions cpp/cmd/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ foreach(CMD ${GUI_CMD_SRCS})
list(REMOVE_ITEM HEADLESS_CMD_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/${CMD})
endforeach(CMD)

add_custom_target(MRtrixCppCommands)

if(MRTRIX_USE_PCH)
file(GENERATE OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/pch_cmd.cpp CONTENT "int main(){}")
add_executable(pch_cmd ${CMAKE_CURRENT_BINARY_DIR}/pch_cmd.cpp)
Expand All @@ -29,6 +31,7 @@ function(add_cmd CMD_SRC IS_GUI)
# Extract the filename without an extension (NAME_WE)
get_filename_component(CMD_NAME ${CMD_SRC} NAME_WE)
add_executable(${CMD_NAME} ${CMD_SRC})
add_dependencies(MRtrixCppCommands ${CMD_NAME})
target_link_libraries(${CMD_NAME} PRIVATE
$<IF:$<BOOL:${IS_GUI}>,mrtrix::gui,mrtrix::core>
mrtrix::executable-version
Expand Down
164 changes: 98 additions & 66 deletions python/mrtrix3/commands/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,90 +1,126 @@

set(PYTHON_COMMANDS_INIT_FILE ${PROJECT_BINARY_DIR}/lib/mrtrix3/commands/__init__.py)

file(GLOB PYTHON_ALL_COMMANDS_ROOT_PATHS
${CMAKE_CURRENT_SOURCE_DIR}/*
"${CMAKE_CURRENT_SOURCE_DIR}/*"
)

file(GLOB_RECURSE PYTHON_ALL_COMMANDS_FILES
${CMAKE_CURRENT_SOURCE_DIR}/*.py
"${CMAKE_CURRENT_SOURCE_DIR}/*.py"
)

# Collect a list of top-level command names (directories or single .py files).
set(PYTHON_COMMAND_LIST "")
foreach(PYTHON_PATH ${PYTHON_ALL_COMMANDS_ROOT_PATHS})
get_filename_component(CMDNAME ${PYTHON_PATH} NAME_WE)
if(NOT ${CMDNAME} STREQUAL "CMakeLists" AND NOT ${CMDNAME} STREQUAL "__init__")
list(APPEND PYTHON_COMMAND_LIST ${CMDNAME})
foreach(PYTHON_PATH IN LISTS PYTHON_ALL_COMMANDS_ROOT_PATHS)
get_filename_component(CMDNAME "${PYTHON_PATH}" NAME_WE)
if(NOT "${CMDNAME}" STREQUAL "CMakeLists" AND NOT "${CMDNAME}" STREQUAL "__init__")
list(APPEND PYTHON_COMMAND_LIST "${CMDNAME}")
endif()
endforeach()

if(MRTRIX_PYTHON_SOFTLINK)
set(PYTHON_COMMANDS_TARGET_NAME "LinkPythonCommandFiles")
set(PYTHON_API_TARGET_NAME "LinkPythonCommandFiles")
set(PYTHON_API_TARGET_NAME "LinkPythonAPIFiles")
set(PYTHON_COMMANDS_FUNCTION_NAME "create_symlink")
else()
set(PYTHON_COMMANDS_TARGET_NAME "CopyPythonCommandFiles")
set(PYTHON_API_TARGET_NAME "CopyPythonCommandFiles")
set(PYTHON_API_TARGET_NAME "CopyPythonAPIFiles")
set(PYTHON_COMMANDS_FUNCTION_NAME "copy_if_different")
endif()

add_custom_target(${PYTHON_COMMANDS_TARGET_NAME} ALL)
add_dependencies(${PYTHON_COMMANDS_TARGET_NAME} ${PYTHON_API_TARGET_NAME})

set(PYTHON_BUILD_COMMAND_FILES "")
# Create commands folder in build directory prior to build phase
add_custom_target(MakePythonCommandsDir
COMMAND ${CMAKE_COMMAND} -E make_directory "${PROJECT_BINARY_DIR}/lib/mrtrix3/commands"
)

# Have to append commands to create all directories
# before commands to symlink files can appear
# Use presence of "__init__.py" as proxy for the need to construct a directory
foreach(PYTHON_SRC_PATH ${PYTHON_ALL_COMMANDS_FILES})
get_filename_component(FILENAME ${PYTHON_SRC_PATH} NAME)
if(${FILENAME} STREQUAL "__init__.py")
file(RELATIVE_PATH REL_PATH ${CMAKE_CURRENT_SOURCE_DIR} ${PYTHON_SRC_PATH})
get_filename_component(DIRNAME ${REL_PATH} DIRECTORY)
add_custom_command(
TARGET ${PYTHON_COMMANDS_TARGET_NAME}
COMMAND ${CMAKE_COMMAND} -E make_directory ${PROJECT_BINARY_DIR}/lib/mrtrix3/commands/${DIRNAME}
)
endif()
endforeach()
set(ALL_PYTHON_COMMAND_TARGETS "")

foreach(PYTHON_SRC_PATH ${PYTHON_ALL_COMMANDS_FILES})
file(RELATIVE_PATH DST_RELPATH ${CMAKE_CURRENT_SOURCE_DIR} ${PYTHON_SRC_PATH})
# Skip "commands/__init__.py";
# this file will be written separately via execution of "commands/__init__.py.in"
if (${DST_RELPATH} STREQUAL "__init__.py")
continue()
endif()
set(DST_BUILDPATH ${PROJECT_BINARY_DIR}/lib/mrtrix3/commands/${DST_RELPATH})
add_custom_command(
TARGET ${PYTHON_COMMANDS_TARGET_NAME}
COMMAND ${CMAKE_COMMAND} -E ${PYTHON_COMMANDS_FUNCTION_NAME} ${PYTHON_SRC_PATH} ${DST_BUILDPATH}
DEPENDS ${PYTHON_SRC_PATH}

# For each command:
# - create its own target
# - ensure corresponding subdirectories are created in build directory (if necessary)
# - copy/symlink Python files
# - create a stub Python launcher
foreach(CMDNAME IN LISTS PYTHON_COMMAND_LIST)
file(GLOB_RECURSE CMD_PY_FILES
"${CMAKE_CURRENT_SOURCE_DIR}/${CMDNAME}/*.py"
"${CMAKE_CURRENT_SOURCE_DIR}/${CMDNAME}.py"
)
get_filename_component(DST_INSTALLDIR ${DST_RELPATH} DIRECTORY)
install(FILES ${PYTHON_SRC_PATH}
PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ
DESTINATION ${CMAKE_INSTALL_LIBDIR}/mrtrix3/commands/${DST_INSTALLDIR}

set(CMD_TARGET "${CMDNAME}")
add_custom_target(${CMD_TARGET})
add_dependencies(${CMD_TARGET}
${PYTHON_API_TARGET_NAME}
MakePythonCommandsDir
MRtrixCppCommands
)
list(APPEND PYTHON_BUILD_COMMAND_FILES ${DST_PATH})
endforeach()

add_custom_target(MakePythonExecutables ALL)
# For Python commands that need to be in a subdirectory, use presence of "__init__.py"
# as proxy for the need to construct a directory
foreach(PY_FILE IN LISTS CMD_PY_FILES)
get_filename_component(FILENAME "${PY_FILE}" NAME)
if("${FILENAME}" STREQUAL "__init__.py")
file(RELATIVE_PATH REL_PATH "${CMAKE_CURRENT_SOURCE_DIR}" "${PY_FILE}")
get_filename_component(DIRNAME "${REL_PATH}" DIRECTORY)
add_custom_command(
TARGET ${CMD_TARGET}
COMMAND ${CMAKE_COMMAND} -E make_directory
"${PROJECT_BINARY_DIR}/lib/mrtrix3/commands/${DIRNAME}"
COMMENT "Creating directory for Python command ${CMDNAME}"
)
endif()
endforeach()

set(PYTHON_BIN_FILES "")
foreach(CMDNAME ${PYTHON_COMMAND_LIST})
# Copy/symlink Python files
foreach(PY_FILE IN LISTS CMD_PY_FILES)
file(RELATIVE_PATH DST_RELPATH "${CMAKE_CURRENT_SOURCE_DIR}" "${PY_FILE}")
set(DST_BUILDPATH "${PROJECT_BINARY_DIR}/lib/mrtrix3/commands/${DST_RELPATH}")

add_custom_command(
TARGET ${CMD_TARGET}
COMMAND ${CMAKE_COMMAND} -E ${PYTHON_COMMANDS_FUNCTION_NAME}
"${PY_FILE}" "${DST_BUILDPATH}"
DEPENDS "${PY_FILE}"
COMMENT "Copying Python script for ${CMDNAME}"
)

get_filename_component(DST_INSTALLDIR "${DST_RELPATH}" DIRECTORY)
install(
FILES "${PY_FILE}"
PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ
DESTINATION "${CMAKE_INSTALL_LIBDIR}/mrtrix3/commands/${DST_INSTALLDIR}"
)
endforeach()

# Create stub Python launcher for the command
add_custom_command(
TARGET MakePythonExecutables
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMAND ${CMAKE_COMMAND} -DCMDNAME=${CMDNAME} -DOUTPUT_DIR="${PROJECT_BINARY_DIR}/bin" -P ${PROJECT_SOURCE_DIR}/cmake/MakePythonExecutable.cmake
TARGET ${CMD_TARGET}
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
COMMAND ${CMAKE_COMMAND}
-DCMDNAME=${CMDNAME}
-DOUTPUT_DIR="${PROJECT_BINARY_DIR}/bin"
-P "${PROJECT_SOURCE_DIR}/cmake/MakePythonExecutable.cmake"
COMMENT "Creating Python stub for ${CMDNAME}"
)
list(APPEND PYTHON_BIN_FILES ${PROJECT_BINARY_DIR}/bin/${CMDNAME})

install(
FILES "${PROJECT_BINARY_DIR}/bin/${CMDNAME}"
PERMISSIONS
OWNER_READ OWNER_WRITE OWNER_EXECUTE
GROUP_READ GROUP_EXECUTE
WORLD_READ WORLD_EXECUTE
DESTINATION "${CMAKE_INSTALL_BINDIR}"
)

list(APPEND ALL_PYTHON_COMMAND_TARGETS "${CMD_TARGET}")
endforeach()

add_custom_target(MRtrixPythonCommands ALL)
add_dependencies(MRtrixPythonCommands ${ALL_PYTHON_COMMAND_TARGETS})

# We need to generate a list of MRtrix3 commands:
# function run.command() does different things if it is executing an MRtrix3 command vs. an external command,
# but unlike prior software versions we cannot simply interrogate the contents of the bin/ directory at runtime
# function run.command() does different things if it is executing an MRtrix3 command vs. an external command,
# but unlike prior software versions we cannot simply interrogate the contents of the bin/ directory at runtime
add_custom_target(MakePythonCommandsInit ALL)
add_dependencies(MakePythonCommandsInit ${PYTHON_API_TARGET_NAME})
add_dependencies(MakePythonCommandsInit PythonCommands)
add_custom_command(
TARGET MakePythonCommandsInit
COMMAND ${CMAKE_COMMAND}
Expand All @@ -94,16 +130,12 @@ add_custom_command(
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
)

add_custom_target(PythonCommands
SOURCES ${PYTHON_ALL_COMMANDS_FILES}
)

install(FILES ${PYTHON_BIN_FILES}
PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE
DESTINATION ${CMAKE_INSTALL_BINDIR}
install(
FILES "${PYTHON_COMMANDS_INIT_FILE}"
PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ
DESTINATION "${CMAKE_INSTALL_LIBDIR}/mrtrix3/commands/"
)

install(FILES ${PYTHON_COMMANDS_INIT_FILE}
PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ
DESTINATION ${CMAKE_INSTALL_LIBDIR}/mrtrix3/commands/
add_custom_target(MRtrixPythonCommandsSources
SOURCES ${PYTHON_ALL_COMMANDS_FILES}
)
Loading