Skip to content

Commit 00614a2

Browse files
jcfrthewtex
authored andcommitted
Add support for building one ITK wheel per "group"
Based on the selected wheel, the script will: (1) update setup.py and (2) remove or configure "itk/__init__.py" Considering that * Every ITK module is associated with exactly on ITK group. * ITK module dependencies are specified independently of ITK groups we semi-arbitrarily defined a collection of wheels (see ``ITK_WHEEL_GROUPS``) that will roughly bundle the modules associated with each group. Accepted values for wheel name are: ``` itk itk-core itk-filtering itk-io itk-numerics itk-registration itk-segmentation ``` Since ``itk-video`` does NOT contain any wrapped module, it is currently not included.
1 parent 8229dc6 commit 00614a2

File tree

8 files changed

+1011
-58
lines changed

8 files changed

+1011
-58
lines changed

CMakeLists.txt

Lines changed: 286 additions & 36 deletions
Large diffs are not rendered by default.

cmake/ITKPythonPackage.cmake

Lines changed: 241 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11

2-
2+
# ipp_ExternalProject_Add_Empty(<proj> <depends>)
3+
#
34
# Add an empty external project
5+
#
46
function(ipp_ExternalProject_Add_Empty proj depends)
57
set(depends_args)
68
if(NOT depends STREQUAL "")
@@ -18,3 +20,241 @@ function(ipp_ExternalProject_Add_Empty proj depends)
1820
${depends_args}
1921
)
2022
endfunction()
23+
24+
# ipp_set_itk_groups()
25+
#
26+
# Set ``ITK_MODULE_*_GROUP`` variable for each modules.
27+
#
28+
macro(ipp_set_itk_groups)
29+
include("${}")
30+
foreach( group ${group_list} )
31+
set( _${group}_module_list )
32+
file( GLOB_RECURSE _${group}_module_files ${ITK_SOURCE_DIR}/Modules/${group}/itk-module.cmake )
33+
foreach( _module_file ${_${group}_module_files} )
34+
file( STRINGS ${_module_file} _module_line REGEX "itk_module[ \n]*\\([ \n]*[A-Za-z0-9]*" )
35+
string( REGEX MATCH "(\\([ \n]*)([A-Za-z0-9]*)" _module_name ${_module_line} )
36+
set( _module_name ${CMAKE_MATCH_2} )
37+
set( _${_module_name}_module_line ${_module_line} )
38+
list( APPEND _${group}_module_list ${_module_name} )
39+
set(ITK_MODULE_${_module_name}_GROUP ${group})
40+
endforeach()
41+
endforeach()
42+
endmacro()
43+
44+
# ipp_get_module_dependees(<itk-module> <output_var>)
45+
#
46+
# Collect all modules depending on ``<itk-module>``.
47+
#
48+
function(ipp_get_module_dependees itk-module output_var)
49+
set(dependees "")
50+
foreach(m_enabled IN LISTS ITK_MODULES_ENABLED)
51+
list(FIND ITK_MODULE_${m_enabled}_DEPENDS ${itk-module} _index)
52+
if(NOT _index EQUAL -1)
53+
list(APPEND dependees ${m_enabled})
54+
endif()
55+
endforeach()
56+
list(REMOVE_DUPLICATES dependees)
57+
set(${output_var} ${dependees} PARENT_SCOPE)
58+
endfunction()
59+
60+
function(_recursive_deps item-type item-category itk-item output_var)
61+
set(_${itk-item}_deps )
62+
foreach(dep IN LISTS ITK_${item-type}_${itk-item}_${item-category})
63+
list(APPEND _${itk-item}_deps ${dep})
64+
_recursive_deps(${item-type} ${item-category} ${dep} _${itk-item}_deps)
65+
endforeach()
66+
list(APPEND ${output_var} ${_${itk-item}_deps})
67+
list(REMOVE_DUPLICATES ${output_var})
68+
set(${output_var} ${${output_var}} PARENT_SCOPE)
69+
endfunction()
70+
71+
# ipp_recursive_module_dependees(<itk-module> <output_var>)
72+
#
73+
# Recursively collect all modules depending on ``<itk-module>``.
74+
#
75+
function(ipp_recursive_module_dependees itk-module output_var)
76+
set(_${itk-module}_deps )
77+
_recursive_deps("MODULE" "DEPENDEES" ${itk-module} ${output_var})
78+
set(${output_var} ${${output_var}} PARENT_SCOPE)
79+
endfunction()
80+
81+
# ipp_is_module_leaf(<itk-module> <output_var>)
82+
#
83+
# If ``<itk-module>`` has no dependencies, set ``<output_var> to 1
84+
# otherwise set ``<output_var> to 0.
85+
#
86+
function(ipp_is_module_leaf itk-module output_var)
87+
set(leaf 1)
88+
foreach(m_enabled IN LISTS ITK_MODULES_ENABLED)
89+
list(FIND ITK_MODULE_${m_enabled}_DEPENDS ${itk-module} _index)
90+
if(NOT _index EQUAL -1)
91+
set(leaf 0)
92+
break()
93+
endif()
94+
endforeach()
95+
set(${output_var} ${leaf} PARENT_SCOPE)
96+
endfunction()
97+
98+
# ipp_is_module_python_wrapped(<itk-module> <output_var>)
99+
#
100+
# If ``<itk-module>`` is wrapped in python, set ``<output_var> to 1
101+
# otherwise set ``<output_var> to 0.
102+
#
103+
function(ipp_is_module_python_wrapped itk-module output_var)
104+
set(wrapped 0)
105+
if(NOT DEFINED ITK_MODULE_${itk-module}_GROUP)
106+
message(AUTHOR_WARNING "Variable ITK_MODULE_${itk-module}_GROUP is not defined")
107+
else()
108+
set(group ${ITK_MODULE_${itk-module}_GROUP})
109+
set(module_folder ${itk-module})
110+
# if any, strip ITK prefix
111+
if(module_folder MATCHES "^ITK.+$")
112+
string(REGEX REPLACE "^ITK(.+)$" "\\1" module_folder ${module_folder})
113+
endif()
114+
if(EXISTS ${ITK_SOURCE_DIR}/Modules/${group}/${itk-module}/wrapping/CMakeLists.txt
115+
OR EXISTS ${ITK_SOURCE_DIR}/Modules/${group}/${module_folder}/wrapping/CMakeLists.txt)
116+
set(wrapped 1)
117+
endif()
118+
endif()
119+
set(${output_var} ${wrapped} PARENT_SCOPE)
120+
endfunction()
121+
122+
# ipp_wheel_to_group(<wheel_name> <group_name_var>)
123+
#
124+
# Extract ITK group name from wheel name (e.g 'itk-core' -> 'Core').
125+
#
126+
# If the group name has less than 3 characters, take the uppercase
127+
# value (e.g 'itk-io' -> 'IO').
128+
#
129+
function(ipp_wheel_to_group wheel_name group_name_var)
130+
string(REPLACE "itk-" "" _group ${wheel_name})
131+
string(SUBSTRING ${_group} 0 1 _first)
132+
string(TOUPPER ${_first} _first_uc)
133+
string(SUBSTRING ${_group} 1 -1 _remaining)
134+
set(group_name "${_first_uc}${_remaining}")
135+
# Convert to upper case if length <= 2
136+
string(LENGTH ${group_name} _length)
137+
if(_length LESS 3)
138+
string(TOUPPER ${group_name} group_name)
139+
endif()
140+
set(${group_name_var} ${group_name} PARENT_SCOPE)
141+
endfunction()
142+
143+
# ipp_pad_text(<text> <text_right_jusitfy_length> <output_var>)
144+
#
145+
# Example:
146+
#
147+
# set(row "Apple")
148+
# ipp_pad_text(${row} 20 row)
149+
#
150+
# set(row "${row}Banana")
151+
# ipp_pad_text(${row} 40 row)
152+
#
153+
# set(row "${row}Kiwi")
154+
# ipp_pad_text(${row} 60 row)
155+
#
156+
# message(${row})
157+
#
158+
# Output:
159+
#
160+
# Apple Banana Kiwi
161+
#
162+
function(ipp_pad_text text text_right_jusitfy_length output_var)
163+
set(fill_char " ")
164+
string(LENGTH "${text}" text_length)
165+
math(EXPR pad_length "${text_right_jusitfy_length} - ${text_length} - 1")
166+
if(pad_length GREATER 0)
167+
string(RANDOM LENGTH ${pad_length} ALPHABET ${fill_char} text_dots)
168+
set(${output_var} "${text} ${text_dots}" PARENT_SCOPE)
169+
else()
170+
set(${output_var} "${text}" PARENT_SCOPE)
171+
endif()
172+
endfunction()
173+
174+
# ipp_display_table_row(<values> <widths>)
175+
#
176+
# Example:
177+
#
178+
# ipp_display_table_row("Apple^^Banana^^Kiwi" "20;20;20")
179+
# ipp_display_table_row("Eiger^^Rainer^^Sajama" "20;20;20")
180+
#
181+
# Output:
182+
#
183+
# Apple Banana Kiwi
184+
# Eiger Rainer Sajama
185+
#
186+
function(ipp_display_table_row values widths)
187+
list(LENGTH values length)
188+
set(text "")
189+
math(EXPR range "${length} - 1")
190+
foreach(index RANGE ${range})
191+
list(GET widths ${index} width)
192+
list(GET values ${index} value)
193+
string(REPLACE "^^" ";" value "${value}")
194+
ipp_pad_text("${value}" ${width} value)
195+
set(text "${text}${value}")
196+
endforeach()
197+
message(STATUS "${text}")
198+
endfunction()
199+
200+
# ipp_list_to_string(<separator> <input_list> <output_string_var>)
201+
#
202+
# Example:
203+
#
204+
# set(values Foo Bar Oof)
205+
# message("${values}")
206+
# ipp_list_to_string("^^" "${values}" values)
207+
# message("${values}")
208+
#
209+
# Output:
210+
#
211+
# Foo;Bar;Oof
212+
# Foo^^Bar^^Oof
213+
#
214+
# Copied from Slicer/CMake/ListToString.cmake
215+
#
216+
function(ipp_list_to_string separator input_list output_string_var)
217+
set(_string "")
218+
# Get list length
219+
list(LENGTH input_list list_length)
220+
# If the list has 0 or 1 element, there is no need to loop over.
221+
if(list_length LESS 2)
222+
set(_string "${input_list}")
223+
else()
224+
math(EXPR last_element_index "${list_length} - 1")
225+
foreach(index RANGE ${last_element_index})
226+
# Get current item_value
227+
list(GET input_list ${index} item_value)
228+
if(NOT item_value STREQUAL "")
229+
# .. and append non-empty value to output string
230+
set(_string "${_string}${item_value}")
231+
# Append separator if current element is NOT the last one.
232+
if(NOT index EQUAL last_element_index)
233+
set(_string "${_string}${separator}")
234+
endif()
235+
endif()
236+
endforeach()
237+
endif()
238+
set(${output_string_var} ${_string} PARENT_SCOPE)
239+
endfunction()
240+
241+
# No-op function allowing to shut-up "Manually-specified variables were not used by the project"
242+
# warnings.
243+
function(ipp_unused_vars)
244+
endfunction()
245+
246+
#
247+
# Unused
248+
#
249+
250+
function(recursive_module_deps itk-module output_var)
251+
set(_${itk-module}_deps )
252+
_recursive_deps("MODULE" "DEPENDS" ${itk-module} ${output_var})
253+
set(${output_var} ${${output_var}} PARENT_SCOPE)
254+
endfunction()
255+
256+
function(recursive_group_deps itk-group output_var)
257+
set(_${itk-group}_deps )
258+
_recursive_deps("GROUP" "DEPENDS" ${itk-group} ${output_var})
259+
set(${output_var} ${${output_var}} PARENT_SCOPE)
260+
endfunction()

scripts/WHEEL_NAMES.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
itk-core
2+
itk-filtering
3+
itk-io
4+
itk-numerics
5+
itk-registration
6+
itk-segmentation

scripts/internal/manylinux-build-wheels.sh

Lines changed: 73 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,21 +25,86 @@ for PYBIN in "${PYBINARIES[@]}"; do
2525
echo "PYTHON_INCLUDE_DIR:${PYTHON_INCLUDE_DIR}"
2626
echo "PYTHON_LIBRARY:${PYTHON_LIBRARY}"
2727

28+
# Install dependencies
2829
${PYBIN}/pip install -r /work/requirements-dev.txt
30+
31+
build_type=MinSizeRel
32+
source_path=/work/standalone-${arch}-build/ITK-source
2933
build_path=/work/ITK-$(basename $(dirname ${PYBIN}))-manylinux1_${arch}
34+
SETUP_PY_CONFIGURE="${script_dir}/../setup_py_configure.py"
35+
3036
# Clean up previous invocations
31-
rm -rf $build_path
32-
${PYBIN}/python setup.py bdist_wheel --build-type MinSizeRel -G Ninja -- \
33-
-DITK_SOURCE_DIR:PATH=/work/standalone-${arch}-build/ITK-source \
34-
-DITK_BINARY_DIR:PATH=${build_path} \
35-
-DPYTHON_EXECUTABLE:FILEPATH=${PYTHON_EXECUTABLE} \
36-
-DPYTHON_INCLUDE_DIR:PATH=${PYTHON_INCLUDE_DIR} \
37-
-DPYTHON_LIBRARY:FILEPATH=${PYTHON_LIBRARY}
38-
${PYBIN}/python setup.py clean
37+
rm -rf ${build_path}
38+
39+
single_wheel=0
40+
41+
if [[ ${single_wheel} == 1 ]]; then
42+
43+
echo "#"
44+
echo "# Build single ITK wheel"
45+
echo "#"
46+
47+
# Configure setup.py
48+
${PYBIN}/python ${SETUP_PY_CONFIGURE} "itk"
49+
# Generate wheel
50+
${PYBIN}/python setup.py bdist_wheel --build-type ${build_type} -G Ninja -- \
51+
-DITK_SOURCE_DIR:PATH=${source_path} \
52+
-DITK_BINARY_DIR:PATH=${build_path} \
53+
-DITKPythonPackage_ITK_BINARY_REUSE:BOOL=OFF \
54+
-DITKPythonPackage_WHEEL_NAME:STRING="itk" \
55+
-DPYTHON_EXECUTABLE:FILEPATH=${PYTHON_EXECUTABLE} \
56+
-DPYTHON_INCLUDE_DIR:PATH=${PYTHON_INCLUDE_DIR} \
57+
-DPYTHON_LIBRARY:FILEPATH=${PYTHON_LIBRARY}
58+
# Cleanup
59+
${PYBIN}/python setup.py clean
60+
61+
else
62+
63+
echo "#"
64+
echo "# Build multiple ITK wheels"
65+
echo "#"
66+
67+
# Build ITK python
68+
(
69+
mkdir -p ${build_path} \
70+
&& cd ${build_path} \
71+
&& cmake \
72+
-DCMAKE_BUILD_TYPE:STRING=${build_type} \
73+
-DITK_SOURCE_DIR:PATH=${source_path} \
74+
-DITK_BINARY_DIR:PATH=${build_path} \
75+
-DPYTHON_EXECUTABLE:FILEPATH=${PYTHON_EXECUTABLE} \
76+
-DPYTHON_INCLUDE_DIR:PATH=${PYTHON_INCLUDE_DIR} \
77+
-DPYTHON_LIBRARY:FILEPATH=${PYTHON_LIBRARY} \
78+
-DWRAP_ITK_INSTALL_COMPONENT_IDENTIFIER:STRING=PythonWheel \
79+
-DWRAP_ITK_INSTALL_COMPONENT_PER_MODULE:BOOL=ON \
80+
-G Ninja \
81+
${source_path} \
82+
&& ninja
83+
)
84+
85+
wheel_names=$(cat ${script_dir}/WHEEL_NAMES.txt)
86+
for wheel_name in ${wheel_names}; do
87+
# Configure setup.py
88+
${PYBIN}/python ${SETUP_PY_CONFIGURE} ${wheel_name}
89+
# Generate wheel
90+
${PYBIN}/python setup.py bdist_wheel --build-type ${build_type} -G Ninja -- \
91+
-DITK_SOURCE_DIR:PATH=${source_path} \
92+
-DITK_BINARY_DIR:PATH=${build_path} \
93+
-DITKPythonPackage_ITK_BINARY_REUSE:BOOL=ON \
94+
-DITKPythonPackage_WHEEL_NAME:STRING=${wheel_name} \
95+
-DPYTHON_EXECUTABLE:FILEPATH=${PYTHON_EXECUTABLE} \
96+
-DPYTHON_INCLUDE_DIR:PATH=${PYTHON_INCLUDE_DIR} \
97+
-DPYTHON_LIBRARY:FILEPATH=${PYTHON_LIBRARY}
98+
# Cleanup
99+
${PYBIN}/python setup.py clean
100+
done
101+
fi
102+
39103
# Remove unecessary files for building against ITK
40104
find $build_path -name '*.cpp' -delete -o -name '*.xml' -delete
41105
rm -rf $build_path/Wrapping/Generators/castxml*
42106
find $build_path -name '*.o' -delete
107+
43108
done
44109

45110
# Since there are no external shared libraries to bundle into the wheels

0 commit comments

Comments
 (0)