Hi everyone,<div><br></div><div>I've been trying to get ITK 4 master to compile on iOS and the Android NDK. This is just a short account of all the problems I faced, and how I worked around them.</div><div><br></div><div>
The SDK versions I'm using are the iOS 5.0 (iPhoneOS5.0.sdk and iPhoneSimulator5.0.sdk), and r7 of the Android NDK. For iOS, my IDE is XCode 4.2, and for Android, it's Eclipse. I'm not sure how other IDEs or SDK versions will impact what I've done.</div>
<div><br></div><div>First, the instructions given at <a href="http://www.midasjournal.org/browse/publication/755">http://www.midasjournal.org/browse/publication/755</a> seem to be outdated. They may work on 3.20, but on 4.x, they fall flat. Most of the modifications suggested are different, and much better accomplished using a CMake toolchain. Also, XCode 4.2 looks very different from the screenshots in the paper. For example, the modification suggested in vnl_math.cxx is just not required. But this paper provides a good starting point to understanding how to do it all.</div>
<div><br></div><div>I chose to do it all using Makefiles instead of XCode or Eclipse projects. This makes it easier to debug the console output. Also, I used toolchain files for each of the builds. So, here's my procedure:</div>
<div><br></div><div>1. Build everything on OSX; this is mostly a sanity check to ensure that the code-base is compilable in its pristine form.</div><div>2. Use the attached toolchain files to configure ITK.</div><div>3. Disable some modules that don't compile - primarily, the ITK-VTK glue, GDCM TIFF and HDF5. I think I know how to solve the first two, at least - the ITK-VTK glue should compile against vtk-modular, which VES for example is using, and TIFF should work with the version of itkmkg3states, as suggested in the paper. But I haven't tried that myself. I don't know how to work with HDF5, and since I'm not using it, it's not my priority.</div>
<div>4. There are a few changes required to the code, in itkFloatingPointExceptions.cxx and itkMutexLockNoThreads.cxx, because the #ifdefs required there are not auto-detected by CMake properly.</div><div>5. Run make</div>
<div><br></div><div>Specific to iOS:</div><div>I used a toolchain, and a build script (runner.sh) which defines CFLAGS and CXXFLAGS. Mostly, it's copied from here: <a href="http://stackoverflow.com/a/6477393/11684">http://stackoverflow.com/a/6477393/11684</a> and modified a bit because it was written for Bullet. Runner.sh itself tries to use the default configuration (ITK_BUILD_ALL_MODULES is ON), so after it finished with errors, I went in with cmake-gui and tweaked the modules to the list that would compile (see above).</div>
<div><br></div><div>I'm not able to figure out if clang defines any iOS specific macros that I can use to conditionally compile code, so I defined -D__APPLE_IOS__ in CFLAGS and CXXFLAGS so that I could make code modifications to itk that would be reasonably portable.</div>
<div><br></div><div>The modification I had to make to the source code itself is in itkFloatingPointExceptions.cxx. The iOS API doesn't define feenableexcept and fedisableexcept (I think those are not part of C99?). So I just defined empty functions returning 0, as under "#if defined(__sun)" in the same file. My modifications look something like this:</div>
<div><br></div><div><div>diff --git a/Modules/Core/Common/src/itkFloatingPointExceptions.cxx b/Modules/Core/Common/src/itkFloatingPointExceptions.cxx</div><div>index 0bce9ce..7115c42 100644</div><div>--- a/Modules/Core/Common/src/itkFloatingPointExceptions.cxx</div>
<div>+++ b/Modules/Core/Common/src/itkFloatingPointExceptions.cxx</div><div>@@ -55,12 +55,7 @@ <a href="http://graphviz.sourcearchive.com/documentation/2.16/gvrender__pango_8c-source.h">http://graphviz.sourcearchive.com/documentation/2.16/gvrender__pango_8c-source.h</a></div>
<div> #endif // LINUX</div><div> </div><div> </div></div><div><div>@@ -427,6 +422,20 @@ void FloatingPointExceptions</div><div> </div><div> #else // defined( _WIN32 )</div><div> </div><div>+#if defined(__APPLE_IOS__)</div>
<div>+// Added for iOS</div><div>+int feenableexcept(unsigned int)</div><div>+{</div><div>+ return 0;</div><div>+}</div><div>+</div><div>+int fedisableexcept(unsigned int)</div><div>+{</div><div>+ return 0;</div><div>+}</div>
<div>+</div><div>+#endif // __APPLE_IOS__</div><div>+</div><div> void</div><div> FloatingPointExceptions</div><div> ::Enable()</div><div><br></div></div><div>Apart from this, no problems with iOS. It builds fine, and just works.</div>
<div><br></div><div>Specific to the iOS simulator:</div><div>The iOS simulator is actually an x86 native platform, and should be built with the appropriate options. The only change from the iOS instructions is to change CFLAGS in runner.sh to 'CFLAGS="-arch i386 -isysroot $SYSROOT -miphoneos-version-min=4.0 -D__APPLE_IOS__"'. Otherwise, everything works just fine.</div>
<div><br></div><div>Specific to Android:</div><div>I hunted around for a good toolchain I could use for Android, and finally after several failures, I tried the one distributed with VES, which itself comes from OpenCV, and this one works out of the box. The other toolchains I found online (<a href="http://code.google.com/p/android-cmake/">http://code.google.com/p/android-cmake/</a> for example) fail when testing for endianness, which had me stuck for a very long time. But the OpenCV toolchain works fine. To use this toolchain, one exports NDK_TOOLCHAIN_ROOT=/path/to/android/ndk in the shell, and then runs cmake with the usual options. Again, I went in and disabled those modules that wouldn't compile - the same ones as for iOS.</div>
<div><br></div><div>Two code changes are required. In itkMutexLockNoThreads.cxx. I don't think this is Android specific; this file seems to doubly define stuff that's already defined in itkMutexLock.cxx. It's out of sync with the other platform-specific itkMutexLock*.cxx files which are its siblings. The code change is below:</div>
<div><div><br></div><div>diff --git a/Modules/Core/Common/src/itkMutexLockNoThreads.cxx b/Modules/Core/Common/src/itkMutexLockNoThreads.cxx</div><div>index b38c2dc..0bb3ead 100644</div><div>--- a/Modules/Core/Common/src/itkMutexLockNoThreads.cxx</div>
<div>+++ b/Modules/Core/Common/src/itkMutexLockNoThreads.cxx</div><div>@@ -29,12 +29,6 @@</div><div> </div><div> namespace itk</div><div> {</div><div>-// New for the SimpleMutex</div><div>-SimpleMutexLock * SimpleMutexLock::New()</div>
<div>-{</div><div>- return new SimpleMutexLock;</div><div>-}</div><div>-</div><div> // Construct a new MutexLock</div><div> SimpleMutexLock::SimpleMutexLock()</div><div> {</div><div>@@ -87,8 +81,4 @@ void SimpleMutexLock::Unlock()</div>
<div> #endif</div><div> }</div><div> </div><div>-void MutexLock::PrintSelf(std::ostream & os, Indent indent) const</div><div>-{</div><div>- Superclass::PrintSelf(os, indent);</div><div>-}</div><div> } //end namespace itk</div>
</div><div><br></div><div>The other code change is in itkFloatingPointExceptions.cxx, to work around a rather strange bug in the Android NDK. In fenv.h, the code starts wtih __BEGIN_DECLS and ends with __END_DECLS, which should be defined to an 'extern "C" {' and '}' when compiling with C++. This is defined in sys/cdefs.h in the NDK include directory ($NDK_ROOT/platforms/android-8/arch-arm/usr/include/sys/cdefs.h in my case). This is not included in fenv.h, so we don't get these definitions, and so when CMake tries to detect the presence of fenv.h, its test compile fails and it sets ITK_HAVE_FENV_H to FALSE. Fixing this would either require changes to the NDK or to CMake, so instead, I just removed the conditional ITK_HAVE_FENV_H in itkFloatingPointExceptions.cxx as below (yeah, it should really be macro based like I did for iOS above, but I got lazy towards the end):</div>
<div><br></div><div><div>diff --git a/Modules/Core/Common/src/itkFloatingPointExceptions.cxx b/Modules/Core/Common/src/itkFloatingPointExceptions.cxx</div><div>index 0bce9ce..7115c42 100644</div><div>--- a/Modules/Core/Common/src/itkFloatingPointExceptions.cxx</div>
<div>+++ b/Modules/Core/Common/src/itkFloatingPointExceptions.cxx</div><div>@@ -55,12 +55,7 @@ <a href="http://graphviz.sourcearchive.com/documentation/2.16/gvrender__pango_8c-source.h">http://graphviz.sourcearchive.com/documentation/2.16/gvrender__pango_8c-source.h</a></div>
<div> #endif // LINUX</div><div> </div><div> </div><div>-#ifdef ITK_HAVE_FENV_H</div><div>-#include <stdio.h> // needed on Solaris</div><div> #include <fenv.h></div><div>-#else</div><div>-#error "fenv.h required for floating point exception handling"</div>
<div>-#endif</div><div> </div><div> #define DEFINED_PPC (defined(__ppc__) || defined(__ppc64__))</div><div> #define DEFINED_INTEL (defined(__i386__) || defined(__x86_64__))</div></div><div><br></div><div>With these changes, everything compiles!</div>
<div><br></div><div>Testing and using my builds:</div><div><br></div><div>For iOS, I've compiled a simple program that just allocates an itk::Image<unsigned char> on a button click. Just a canonical hello world... I've run this on the simulator, but not on the device as of now (I'm yet to pay the $99 Apple Tax).</div>
<div><br></div><div>A small note: The UIImage import/export classes at <a href="http://www.midasjournal.org/browse/publication/760">http://www.midasjournal.org/browse/publication/760</a> seem to be a rather roundabout way of doing things! Here's a code example from that paper:</div>
<div><br></div><div><pre style="margin-top:0px;margin-bottom:0px;padding-top:0px;padding-right:0px;padding-bottom:0px;padding-left:0px;border-top-width:0px;border-right-width:0px;border-bottom-width:0px;border-left-width:0px;border-style:initial;border-color:initial;font:inherit;font-family:'Bitstream Vera Sans Mono',Courier,monospace;line-height:1.4;color:rgb(51,51,51)">
<div class="line" id="LC87" style="margin-top:0px;margin-right:0px;margin-bottom:0px;margin-left:0px;padding-top:0px;padding-right:0px;padding-bottom:0px;padding-left:1em;border-top-width:0px;border-right-width:0px;border-bottom-width:0px;border-left-width:0px;border-style:initial;border-color:initial;font:inherit">
                <span class="n" style="margin-top:0px;margin-right:0px;margin-bottom:0px;margin-left:0px;padding-top:0px;padding-right:0px;padding-bottom:0px;padding-left:0px;border-top-width:0px;border-right-width:0px;border-bottom-width:0px;border-left-width:0px;border-style:initial;border-color:initial;font-size:12px;font:inherit">grayReader</span><span class="o" style="margin-top:0px;margin-right:0px;margin-bottom:0px;margin-left:0px;padding-top:0px;padding-right:0px;padding-bottom:0px;padding-left:0px;border-top-width:0px;border-right-width:0px;border-bottom-width:0px;border-left-width:0px;border-style:initial;border-color:initial;font-size:12px;font:inherit;font-weight:bold">-></span><span class="n" style="margin-top:0px;margin-right:0px;margin-bottom:0px;margin-left:0px;padding-top:0px;padding-right:0px;padding-bottom:0px;padding-left:0px;border-top-width:0px;border-right-width:0px;border-bottom-width:0px;border-left-width:0px;border-style:initial;border-color:initial;font-size:12px;font:inherit">SetImageIO</span><span class="p" style="margin-top:0px;margin-right:0px;margin-bottom:0px;margin-left:0px;padding-top:0px;padding-right:0px;padding-bottom:0px;padding-left:0px;border-top-width:0px;border-right-width:0px;border-bottom-width:0px;border-left-width:0px;border-style:initial;border-color:initial;font-size:12px;font:inherit">(</span><span class="n" style="margin-top:0px;margin-right:0px;margin-bottom:0px;margin-left:0px;padding-top:0px;padding-right:0px;padding-bottom:0px;padding-left:0px;border-top-width:0px;border-right-width:0px;border-bottom-width:0px;border-left-width:0px;border-style:initial;border-color:initial;font-size:12px;font:inherit">imageIO1</span><span class="p" style="margin-top:0px;margin-right:0px;margin-bottom:0px;margin-left:0px;padding-top:0px;padding-right:0px;padding-bottom:0px;padding-left:0px;border-top-width:0px;border-right-width:0px;border-bottom-width:0px;border-left-width:0px;border-style:initial;border-color:initial;font-size:12px;font:inherit">);</span></div>
<div class="line" id="LC88" style="margin-top:0px;margin-right:0px;margin-bottom:0px;margin-left:0px;padding-top:0px;padding-right:0px;padding-bottom:0px;padding-left:1em;border-top-width:0px;border-right-width:0px;border-bottom-width:0px;border-left-width:0px;border-style:initial;border-color:initial;font:inherit">
                <span class="n" style="margin-top:0px;margin-right:0px;margin-bottom:0px;margin-left:0px;padding-top:0px;padding-right:0px;padding-bottom:0px;padding-left:0px;border-top-width:0px;border-right-width:0px;border-bottom-width:0px;border-left-width:0px;border-style:initial;border-color:initial;font-size:12px;font:inherit">imageIO1</span><span class="o" style="margin-top:0px;margin-right:0px;margin-bottom:0px;margin-left:0px;padding-top:0px;padding-right:0px;padding-bottom:0px;padding-left:0px;border-top-width:0px;border-right-width:0px;border-bottom-width:0px;border-left-width:0px;border-style:initial;border-color:initial;font-size:12px;font:inherit;font-weight:bold">-></span><span class="n" style="margin-top:0px;margin-right:0px;margin-bottom:0px;margin-left:0px;padding-top:0px;padding-right:0px;padding-bottom:0px;padding-left:0px;border-top-width:0px;border-right-width:0px;border-bottom-width:0px;border-left-width:0px;border-style:initial;border-color:initial;font-size:12px;font:inherit">SetFileName</span><span class="p" style="margin-top:0px;margin-right:0px;margin-bottom:0px;margin-left:0px;padding-top:0px;padding-right:0px;padding-bottom:0px;padding-left:0px;border-top-width:0px;border-right-width:0px;border-bottom-width:0px;border-left-width:0px;border-style:initial;border-color:initial;font-size:12px;font:inherit">(</span><span class="n" style="margin-top:0px;margin-right:0px;margin-bottom:0px;margin-left:0px;padding-top:0px;padding-right:0px;padding-bottom:0px;padding-left:0px;border-top-width:0px;border-right-width:0px;border-bottom-width:0px;border-left-width:0px;border-style:initial;border-color:initial;font-size:12px;font:inherit">image</span><span class="p" style="margin-top:0px;margin-right:0px;margin-bottom:0px;margin-left:0px;padding-top:0px;padding-right:0px;padding-bottom:0px;padding-left:0px;border-top-width:0px;border-right-width:0px;border-bottom-width:0px;border-left-width:0px;border-style:initial;border-color:initial;font-size:12px;font:inherit">.</span><span class="n" style="margin-top:0px;margin-right:0px;margin-bottom:0px;margin-left:0px;padding-top:0px;padding-right:0px;padding-bottom:0px;padding-left:0px;border-top-width:0px;border-right-width:0px;border-bottom-width:0px;border-left-width:0px;border-style:initial;border-color:initial;font-size:12px;font:inherit">image</span><span class="p" style="margin-top:0px;margin-right:0px;margin-bottom:0px;margin-left:0px;padding-top:0px;padding-right:0px;padding-bottom:0px;padding-left:0px;border-top-width:0px;border-right-width:0px;border-bottom-width:0px;border-left-width:0px;border-style:initial;border-color:initial;font-size:12px;font:inherit">);</span></div>
<div class="line" id="LC89" style="margin-top:0px;margin-right:0px;margin-bottom:0px;margin-left:0px;padding-top:0px;padding-right:0px;padding-bottom:0px;padding-left:1em;border-top-width:0px;border-right-width:0px;border-bottom-width:0px;border-left-width:0px;border-style:initial;border-color:initial;font:inherit">
                <span class="n" style="margin-top:0px;margin-right:0px;margin-bottom:0px;margin-left:0px;padding-top:0px;padding-right:0px;padding-bottom:0px;padding-left:0px;border-top-width:0px;border-right-width:0px;border-bottom-width:0px;border-left-width:0px;border-style:initial;border-color:initial;font-size:12px;font:inherit">grayReader</span><span class="o" style="margin-top:0px;margin-right:0px;margin-bottom:0px;margin-left:0px;padding-top:0px;padding-right:0px;padding-bottom:0px;padding-left:0px;border-top-width:0px;border-right-width:0px;border-bottom-width:0px;border-left-width:0px;border-style:initial;border-color:initial;font-size:12px;font:inherit;font-weight:bold">-></span><span class="n" style="margin-top:0px;margin-right:0px;margin-bottom:0px;margin-left:0px;padding-top:0px;padding-right:0px;padding-bottom:0px;padding-left:0px;border-top-width:0px;border-right-width:0px;border-bottom-width:0px;border-left-width:0px;border-style:initial;border-color:initial;font-size:12px;font:inherit">SetFileName</span><span class="p" style="margin-top:0px;margin-right:0px;margin-bottom:0px;margin-left:0px;padding-top:0px;padding-right:0px;padding-bottom:0px;padding-left:0px;border-top-width:0px;border-right-width:0px;border-bottom-width:0px;border-left-width:0px;border-style:initial;border-color:initial;font-size:12px;font:inherit">(</span><span class="s" style="margin-top:0px;margin-right:0px;margin-bottom:0px;margin-left:0px;padding-top:0px;padding-right:0px;padding-bottom:0px;padding-left:0px;border-top-width:0px;border-right-width:0px;border-bottom-width:0px;border-left-width:0px;border-style:initial;border-color:initial;font-size:12px;font:inherit;color:rgb(221,17,68)">"UIImage"</span><span class="p" style="margin-top:0px;margin-right:0px;margin-bottom:0px;margin-left:0px;padding-top:0px;padding-right:0px;padding-bottom:0px;padding-left:0px;border-top-width:0px;border-right-width:0px;border-bottom-width:0px;border-left-width:0px;border-style:initial;border-color:initial;font-size:12px;font:inherit">);</span></div>
<div class="line" id="LC90" style="margin-top:0px;margin-right:0px;margin-bottom:0px;margin-left:0px;padding-top:0px;padding-right:0px;padding-bottom:0px;padding-left:1em;border-top-width:0px;border-right-width:0px;border-bottom-width:0px;border-left-width:0px;border-style:initial;border-color:initial;font:inherit">
                <span class="n" style="margin-top:0px;margin-right:0px;margin-bottom:0px;margin-left:0px;padding-top:0px;padding-right:0px;padding-bottom:0px;padding-left:0px;border-top-width:0px;border-right-width:0px;border-bottom-width:0px;border-left-width:0px;border-style:initial;border-color:initial;font-size:12px;font:inherit">grayReader</span><span class="o" style="margin-top:0px;margin-right:0px;margin-bottom:0px;margin-left:0px;padding-top:0px;padding-right:0px;padding-bottom:0px;padding-left:0px;border-top-width:0px;border-right-width:0px;border-bottom-width:0px;border-left-width:0px;border-style:initial;border-color:initial;font-size:12px;font:inherit;font-weight:bold">-></span><span class="n" style="margin-top:0px;margin-right:0px;margin-bottom:0px;margin-left:0px;padding-top:0px;padding-right:0px;padding-bottom:0px;padding-left:0px;border-top-width:0px;border-right-width:0px;border-bottom-width:0px;border-left-width:0px;border-style:initial;border-color:initial;font-size:12px;font:inherit">Update</span><span class="p" style="margin-top:0px;margin-right:0px;margin-bottom:0px;margin-left:0px;padding-top:0px;padding-right:0px;padding-bottom:0px;padding-left:0px;border-top-width:0px;border-right-width:0px;border-bottom-width:0px;border-left-width:0px;border-style:initial;border-color:initial;font-size:12px;font:inherit">();</span></div>
<div><br></div></pre></div><div>Setting a file name as "UIImage" to a reader object?</div><div><br></div><div>I'd suggest that doing something like what the itk-vtk glue does would be much better. Having an itk::ImageToUIImage and an itk::UIImageToImage pair would be a lot more symmetric in my opinion. I'm going to try that sometime.</div>
<div><br></div><div>I haven't yet tried it out on Android. Once I write a more substantial application, I'll post again.</div><div><br></div><div>So, summing up, some observations and questions:</div><div>1. Cross-compilation was actually _easier_ than I expected, as long as toolchains were used. But it could be easier. Maybe we should have the toolchains included with ITK, like VES is doing?</div>
<div>2. The resources on the web are outdated! They need to be updated...</div><div>3. Some code changes are required, which need to be reviewed.</div><div>4. What does itkmkg3states in itkTIFF actually do? Can we find another way to do what it does while cross-compiling? It seems to me that this would be a persistent problem for all cross-compiled builds (even, for example, MinGW compiling on a Linux host).</div>
<div>5. We should try with vtk-modular and see if the itk-vtk glue can't be coaxed to compile.</div><div>6. What about VES integration?</div><div>7. In general, I suggest setting CMAKE_INSTALL_PREFIX and installing the headers and libraries. This makes it MUCH easier to configure XCode (and I'm sure, the Android NDK also).</div>
<div><br></div><div>In all the above, I was just following from one step to another. I'm sure the process could be simplified and improved. Anyone else have ideas on this account?</div><div><br></div><div>One thing I should really do is go back and edit the iOS toolchain to include CFLAGS and CXXFLAGS like the Android one does. This makes things a LOT simpler. Also, it would be good if we could auto-detect those modules that cannot compile, and disable them. It's a pain, having to go back and reconfigure everything.</div>
<div><br></div><div>Maybe we should write this up as a Wiki article? I'd like it if someone else could replicate these results, so that I know that they are repeatable.</div><div><br></div><div>Hope this helps others...</div>
<div><br></div><div>Shash</div>