= 🛠 Third-party dependency changes =
We upgraded quite a few dependencies this time as we are approaching the next official release of MITK in the beginning of 2021. The most notable upgrade is the replacement of the obsolete TinyXML v2.6.2 library with TinyXML-2 v8.0.0, since it induces critical API-breaking changes (see the migration guide further below).
| Dependency | Old version | New version |
| --- | --- | --- |
| Boost | 1.70 | 1.74 |
| cpprestsdk | 2.10.10 | 2.10.16 |
| CMake (minimum required version) | 3.14.5 | 3.18 |
| CppUnit | 1.12.1 | 1.15.1 |
| CTK | `78341aba` (Dec 07, 2019) | `7210c5bc` (Nov 08, 2020) |
| DCMQI | `ea4f0809` (Jan 23, 2020) | `99192b76` (Nov 06, 2020) |
| GDCM | 3.0.4 | 3.0.8 |
| ITK | `e53d1d94` (May 22, 2020) | 4.13.3 |
| Qwt | 6.1.0 | 6.1.5 |
| ~~TinyXML~~ | 2.6.2 | |
| TinyXML-2 | | 8.0.0 |
= ✨ New features =
- Improved performance for image interactions in 2d render windows
- Windows installers/packages now include shortcuts in their top-level directory to easily start MITK applications
- Windows installers do not need to include/install Microsoft Visual C++ Redistributables anymore
= 🐛 Bugfixes =
- Fixed loading (including query/retrieve) of many DICOM files in MITK installers for Linux and macOS
- Fixed inconsistency of checkable render windows menu items
- Segmentation tools:
- Fixed clearing of preview in Fast Marching 3D tool
- Made Live Wire tool aware of active label
- Early-out when flawed CMake versions 3.19.0 or 3.19.1 are used to configure MITK (CMake v3.19.2 works again)
- Overhauled use of `PACKAGE_DEPENDS` for many third-party dependencies (see API-breaking changes below)
- Patched VTK v9.0.0 to compile with GCC 10
- Fixed MatchPointRegistration to compile with Clang 11
- Fixed packaging of OpenSSL binaries on Windows (e.g. used by XNAT) by introducing the two CMake cache variables `MITK_OPENSSL_SSL_DLL` and `MITK_OPENSSL_CRYPTO_DLL` to be able to explicitly set the paths of the OpenSSL binaries
- Fixed random fails of mitkRESTClientTest
- CDash script fetches tags on each update now
= 🔥 API-breaking changes =
== Minor changes ==
- The `LayoutDesignListChanged()` method of `QmitkRenderWindow` was renamed to `UpdateLayoutDesignList()`
== ITK dependencies ==
ITK dependencies were completely redone for all modules and plugins to only include necessary dependencies and to only expose the necessary subset of those dependencies to dependers. In case of linker errors involving ITK, you probably have to explicitly add a certain ITK module dependency to your MITK module or plugin that you were infected with by other MITK modules in previous versions of MITK.
== `PACKAGE_DEPENDS` (third-party dependencies) ==
`PACKAGE_DEPENDS` now properly supports Boost, OpenMesh, OpenMP, OpenSSL, and Python3. For example:
- `PACKAGE_DEPENDS Boost` is short for `Boost|boost` (header-only part of Boost)
- `PACKAGE_DEPENDS Boost|date_time+random` maps to `Boost::date_time Boost::random`
- `PACKAGE_DEPENDS OpenMesh|Tools` maps to `OpenMeshTools`
- `PACKAGE_DEPENDS OpenMP` maps to `OpenMP::OpenMP_CXX`
- `PACKAGE_DEPENDS OpenSSL|SSL` maps to `OpenSSL::SSL`
- `PACKAGE_DEPENDS Python3|NumPy` maps to `Python3::NumPy`
ITK, OpenGL, Poco and Qt5 dependencies are now based on targets instead of directories as well.
Also, `TARGET_DEPENDS` was introduced as option to bypass the `PACKAGE_DEPENDS` mechanism by passing dependencies directly to `target_link_libraries()`. The option was added to:
- `mitk_create_module()`
- `mitk_create_plugin()`
- `mitk_create_executable()`
- `MITK_CREATE_MODULE_TESTS()`
- `mitkFunctionCreateCommandLineApp()`
- `mitkFunctionCreateMatchPointDeployedAlgorithm()`
== Removal of Biophotonics and Photoacoustics ==
Both Biophotonics and Photoacoustics were experimental components of MITK that were no longer maintained for quite a while. They are removed without replacement.
==== Removed modules ====
- mitkBiophotonics
- MitkBiophotonicsHardware
- MitkSpectroCam
- MitkPhotoacousticsAlgorithms
- MitkPhotoacousticsHardware
- MitkPhotoacousticsLib
- MitkUSHardwareDiPhAS
==== Removed plugins ====
- org.mitk.gui.qt.lasercontrol
- org.mitk.gui.qt.photoacoustics.imageprocessing
- org.mitk.gui.qt.photoacoustics.pausmotioncompensation
- org.mitk.gui.qt.photoacoustics.pausviewer
- org.mitk.gui.qt.photoacoustics.simulation
- org.mitk.gui.qt.photoacoustics.spectralunmixing
- org.mitk.gui.qt.spectrocamrecorder
==== Other removed classes ====
- mitk::USDiPhASDeviceCustomControls
- QmitkUSControlsCustomDiPhASDeviceWidget
== TinyXML to TinyXML-2 migration guide ==
WARNING: While the APIs of TinyXML and TinyXML-2 are similar enough for a straight forward migration except for a few edge cases, it usually affects critical code. If not done thoroughly, migration may result in silently writing valid XML yet corrupt file formats (for example unexpected values). Beware of the few pitfalls! :warning:
=== TinyXML package dependencies of MITK modules ===
```lang=diff
mitk_create_module(
- PACKAGE_DEPENDS PRIVATE tinyxml
+ PACKAGE_DEPENDS PRIVATE tinyxml2
)
```
=== TinyXML header file ===
```lang=diff
- #include <tinyxml.h>
+ #include <tinyxml2.h>
```
=== TinyXML forward declarations ===
```lang=diff
- class TiXmlDocument;
- class TiXmlElement;
- class TiXmlNode;
+ namespace tinyxml2
+ {
+ class XMLDocument;
+ class XMLElement;
+ class XMLNode;
+ }
```
=== TinyXML class names ===
See the forward declarations above to get an idea of the mapping between TinyXML class names and TinyXML-2 class names.
==== Use auto ====
In most cases when traversing or iterating through an XML document, a simple type replacement with the auto keyword is enough. You don't have to care if it is an element or a node - it just works.
```lang=diff
- TiXmlElement* elem = parentElem->FirstChildElement();
+ auto* elem = parentElem->FirstChildElement();
```
=== TinyXML enumeration values ===
```lang=diff
- TIXML_SUCCESS
+ tinyxml2::XML_SUCCESS
```
=== TinyXML class instantiation and object lifetime ===
TinyXML-2 objects like elements or nodes are //always// owned by a document instance. Do not use `new` or `delete` anymore. TinyXML-2 objects that are not part of a document are usually created on the stack like `tinyxml2::Printer printer;`.
```lang=diff
- TiXmlElement* elem = new TiXmlElement("MyElement");
- parentElem->LinkEndChild(elem); // now owned by TinyXML
-
- TiXmlElement* anotherElem = new TiXmlElement("MyOtherElement");
- delete anotherElem;
+ {
+ tinyxml2::XMLDocument doc;
+
+ auto* elem = doc.NewElement("MyElement");
+ parentElem->InsertEndChild(elem);
+
+ auto* anotherElem = doc.NewElement("MyOtherElement");
+ } // doc is running out of scope and deletes all associated objects
```
To be clear, don't do:
```counterexample, lang=cpp, name="Incorrect 🚫"
tinyxml2::XMLElement* CreateMyElement()
{
tinyxml2::XMLDocument doc;
auto* elem = doc.NewElement("MyElement");
return elem;
}
```
Instead, do:
```lang=cpp, name="Correct ✔️"
tinyxml2::XMLElement* CreateMyElement(const tinyxml2::XMLDocument& doc)
{
auto* elem = doc.NewElement("MyElement");
return elem;
}
```
=== TinyXML methods ===
==== Search and replace ====
Most commonly used method names are equal. The most prominent method names to replace are:
```lang=diff
- LinkEndChild
+ InsertEndChild
- SetDoubleAttribute
+ SetAttribute
```
==== :warning: std::string vs const char* ====
While TinyXML supports `std::string` parameters, TinyXML-2 only supports plain C-strings.
```lang=diff
- doc.LoadFile(filename); // std::string
+ doc.LoadFile(filename.c_str());
```
Don't initialize a `std::string` with a `const char*` return type. This is already true for TinyXML but during migration we found several counterexamples. `const char*` can be a `nullptr` but initializing a `std::string` with `nullptr` provokes undefined behavior.
```lang=diff
- std::string attr = elem->Attribute("MyAttribute"); // undefined behavior
+ const char* c_attr = elem->Attribute("MyAttribute");
+ if (c_attr != nullptr)
+ {
+ std::string attr = c_attr;
+ // ...
+ }
```
==== :warning: Implicit bool conversion of XMLError return types (and other return type changes) ====
`tinyxml2::XMLError` is now returned by some methods instead of `bool`. It is an enumeration and its `tinyxml2::XML_SUCCESS` value equals zero. Casting a zero integer to `bool` evaluates to `false`, resulting in inverted logic. Typical candidates during the migration were the `<Load|Save>File()` and `Parse()` methods of `TiXmlDocument`/`tinyxml2::XMLDocument`.
```lang=diff
- TiXmlDocument doc(filename);
- bool success = doc.LoadFile();
// or
- TiXmlDocument doc;
- bool success = doc.LoadFile(filename); // Dangerous! May still compile but logic is inverted.
+ tinyxml2::XMLDocument doc;
+ bool success = tinyxml2::XML_SUCCESS == doc.LoadFile(filename.c_str());
```
=== :warning: Parsing XML strings and streams ===
You cannot stream into an XML document anymore. Instead, parse explicitly. Also notice the completely changed signature of `tinyxml2::XMLDocument::Parse()`. The method doesn't return the parsed contents or a `nullptr` anymore.
```lang=diff
std::string xmlString(std::istreambuf_iterator<char>(xmlStream), {}); // read everything into string
- TiXmlDocument doc;
- xmlStream >> doc;
// or
- if (doc.Parse(xmlString.c_str())) // Dangerous! Still compiles but logic is inverted.
- {
- // ...
- }
+ tinyxml2::XMLDocument doc;
+ if (tinyxml2::XML_SUCCESS == doc.Parse(xmlString.c_str()))
+ {
+ // ...
+ }
```
=== XML document to string/stream conversion ===
There were several ways to print or stream an XML document but now it boils down to:
```lang=cpp
const char* PrintXMLDocument(const tinyxml2::XMLDocument& doc)
{
tinyxml2::XMLPrinter printer;
doc.Print(&printer);
return printer.CStr();
}
```
=== XML declarations ===
```lang=diff
- TiXmlDocument doc;
- doc.LinkEndChild(new TiXmlDeclaration("1.0", "", ""));
+ tinyxml2::Document doc;
+ doc.InsertEndChild(doc.NewDeclaration());
```
=== :warning: Reading and writing boolean attributes ===
TinyXML only supported //reading// boolean attributes, accepting values like `0`, `1`, `"false"`, `"true"`, `"yes"`, and `"no"`. Boolean values were either written implicitly or explicitly as integers or explicitly as strings (`"true"` or `"false"`). The implicit case is the dangerous one as it continues to compile with different results at runtime.
To stay compatible to current file formats, we usually want to emulate the old behavior.
```lang=diff
- elem->SetAttribute("MyAttribute", booleanValue); // Implicitly casted to integer
+ elem->SetAttribute("MyAttribute", static_cast<int>(booleanValue)); // Important! Keep old behavior.
elem->QueryBoolAttribute("MyAttribute", &booleanValue);
```
The string case mentioned above can be optimizied now as TinyXML-2 produces the same strings for boolean values.
```lang=diff
- elem->SetAttribute("MyAttribute", booleanValue ? "true" : "false");
+ elem->SetAttribute("MyAttribute", booleanValue);
```
=== Reading and writing size_t and similar types ===
One some platforms, `size_t` cannot be unambiguously implicitly casted as second parameter of `SetAttribute()`. Check how the value is read in (as integer in most cases) and explicitly cast accordingly.
```lang=diff
- elem->SetAttribute("MyAttribute", vector.size());
+ elem->SetAttribute("MyAttribute", static_cast<int>(vector.size()));
QueryIntAttribute("MyAttribute", &value);
```
=== Public MITK API changes ===
Basically replace TinyXML parameter types with corresponding TinyXML-2 types. We used the opportuniy to improve const-correctness. Methods that create new instances of XML classes now have an additional document input parameter, which is used to manage the lifetime of newly created instances.
The `tinyxml2.h` header file is only included in implementation files. MITK header files contain forward declarations when necessary.
A few examples are:
```lang=diff
- TiXmlElement* Serialize() override;
+ tinyxml2::XMLElement* Serialize(tinyxml2::XMLDocument& doc) override;
- BaseProperty::Pointer Deserialize(TiXmlElement* element) override;
+ BaseProperty::Pointer Deserialize(const tinyxml2::XMLElement* element) override;
- static TiXmlElement* GetLabelAsTiXmlElement(Label* label);
+ static tinyxml2::XMLElement* GetLabelAsXMLElement(tinyxml2::XMLDocument& doc, Label* label);
```