Skip to content
Draft
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
19 changes: 19 additions & 0 deletions Documentation/docs/migration_guides/itk_6_migration_guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -1218,3 +1218,22 @@ afterwards raises `RuntimeError` rather than crashing.

Calls to `OverrideBoundaryCondition` on any *other* class are unaffected — those
never took ownership and keep their current behavior.

## `ITK_AUTOLOAD_PATH` is read once; use `ObjectFactoryBase::SetAutoloadPath`

The `ITK_AUTOLOAD_PATH` environment variable lists the directories ITK scans for
shared libraries that register dynamically loaded object factories (IO plugins,
for example). Entries are separated by `:` (`;` on Windows). The scan happens
lazily, the first time a factory is used, and only when ITK is built with
`ITK_USE_AUTOLOAD_PATH` (dynamic loading enabled, not a Python-wrapped build).

The autoload path is now stored in `ObjectFactoryBase` global state, initialized
from the `ITK_AUTOLOAD_PATH` environment variable when that state is created.
`ObjectFactoryBase::SetAutoloadPath()` and `GetAutoloadPath()` set and query it
at runtime. `ReHash()` no longer re-reads the environment.

### What you need to do

Code that calls `putenv("ITK_AUTOLOAD_PATH=...")` after startup and then
`ObjectFactoryBase::ReHash()` must call `ObjectFactoryBase::SetAutoloadPath(...)`
instead. When factories were already initialized, follow it with `ReHash()`.
18 changes: 16 additions & 2 deletions Modules/Core/Common/include/itkObjectFactoryBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,22 @@ class ITKCommon_EXPORT ObjectFactoryBase : public Object
static std::list<LightObject::Pointer>
CreateAllInstance(const char * itkclassname);

/** Re-check the ITK_AUTOLOAD_PATH for new factory libraries.
* This calls UnRegisterAll before re-loading. */
/** Set/Get the path list searched for dynamically loadable factories.
* The value is initialized from the ITK_AUTOLOAD_PATH environment variable
* when the global state is created. Set it before the factories are first
* used (or call ReHash() afterwards) for the new value to take effect.
Comment thread
hjmjohnson marked this conversation as resolved.
* Paths are separated by a colon (semi-colon on win32).
* The path is only stored, and has no effect, when dynamic factory loading
* is disabled (ITK_USE_AUTOLOAD_PATH is OFF, e.g. in Python-wrapped builds). */
static void
SetAutoloadPath(const std::string & path);
static std::string
GetAutoloadPath();

/** Re-check the autoload path (see SetAutoloadPath()) for new factory
Comment thread
hjmjohnson marked this conversation as resolved.
* libraries. This calls UnRegisterAll before re-loading.
* The environment is not re-read: changing ITK_AUTOLOAD_PATH after startup
* requires SetAutoloadPath(). */
static void
ReHash();

Expand Down
35 changes: 28 additions & 7 deletions Modules/Core/Common/src/itkObjectFactoryBase.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,17 @@ SynchronizeList(FactoryListType & output, FactoryListType & input, bool internal
}
}
}

std::string
GetAutoloadPathFromEnvironment()
{
std::string path;
if (!itksys::SystemTools::GetEnv("ITK_AUTOLOAD_PATH", path))
{
path.clear();
}
return path;
}
} // end of anonymous namespace

namespace itk
Expand Down Expand Up @@ -125,6 +136,7 @@ class ObjectFactoryBase::ObjectFactoryBasePrivate : public LightObject
FactoryListType m_InternalFactories{};
std::atomic<bool> m_Initialized{ false };
bool m_StrictVersionChecking{ false };
std::string m_AutoloadPath{ GetAutoloadPathFromEnvironment() };
};

auto
Expand Down Expand Up @@ -184,6 +196,19 @@ ObjectFactoryBase::GetStrictVersionChecking()
return m_PimplGlobals->m_StrictVersionChecking;
}

void
ObjectFactoryBase::SetAutoloadPath(const std::string & path)
{
itkInitGlobalsMacro(PimplGlobals);
m_PimplGlobals->m_AutoloadPath = path;
Comment thread
hjmjohnson marked this conversation as resolved.
}

std::string
ObjectFactoryBase::GetAutoloadPath()
{
itkInitGlobalsMacro(PimplGlobals);
return m_PimplGlobals->m_AutoloadPath;
}

/**
* Create an instance of a named ITK object using the loaded
Expand Down Expand Up @@ -259,12 +284,8 @@ ObjectFactoryBase::LoadDynamicFactories()
const char PathSeparator = ':';
# endif

const std::string itk_autoload_env{ "ITK_AUTOLOAD_PATH" };
std::string LoadPath;
if (!itksys::SystemTools::GetEnv(itk_autoload_env, LoadPath))
{
return;
}
itkInitGlobalsMacro(PimplGlobals);
const std::string LoadPath = m_PimplGlobals->m_AutoloadPath;
if (LoadPath.empty())
{
return;
Expand Down Expand Up @@ -452,7 +473,7 @@ ObjectFactoryBase::LoadLibrariesInPath(const char * path)
}

/**
* Recheck the ITK_AUTOLOAD_PATH for new libraries
* Recheck the autoload path for new libraries
*/
void
ObjectFactoryBase::ReHash()
Expand Down
34 changes: 34 additions & 0 deletions Modules/Core/Common/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,40 @@ if(ITK_BUILD_SHARED_LIBS AND ITK_USE_AUTOLOAD_PATH)
${ITK_TEST_OUTPUT_DIR}
)
add_dependencies(ITKCommon2TestDriver FactoryTestLib)

# Standalone tests: the autoload path must be configurable before the
# factories are dynamically loaded, either by environment or by API.
add_executable(
itkObjectFactoryAutoloadPathTest
itkObjectFactoryAutoloadPathTest.cxx
)
itk_module_target_label(itkObjectFactoryAutoloadPathTest)
target_link_libraries(
itkObjectFactoryAutoloadPathTest
PUBLIC
${ITKCommon_LIBRARIES}
)
add_dependencies(itkObjectFactoryAutoloadPathTest FactoryTestLib)
itk_add_test(
NAME itkObjectFactoryAutoloadPathEnvTest
COMMAND
itkObjectFactoryAutoloadPathTest
env
$<TARGET_FILE_DIR:FactoryTestLib>
)
set_tests_properties(
itkObjectFactoryAutoloadPathEnvTest
PROPERTIES
ENVIRONMENT
"ITK_AUTOLOAD_PATH=$<TARGET_FILE_DIR:FactoryTestLib>"
)
itk_add_test(
NAME itkObjectFactoryAutoloadPathApiTest
COMMAND
itkObjectFactoryAutoloadPathTest
api
$<TARGET_FILE_DIR:FactoryTestLib>
)
endif()

itk_add_test(
Expand Down
88 changes: 88 additions & 0 deletions Modules/Core/Common/test/itkObjectFactoryAutoloadPathTest.cxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/*=========================================================================
*
* Copyright NumFOCUS
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0.txt
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*=========================================================================*/

#include "itkImportImageContainer.h"
#include "itkObjectFactoryBase.h"
#include "itksys/SystemTools.hxx"

#include <cstdlib>
#include <cstring>
#include <iostream>
#include <string>

// Usage: itkObjectFactoryAutoloadPathTest <env|api> <FactoryLibraryDirectory>
//
// env: ITK_AUTOLOAD_PATH is set by the test harness before the process starts;
// nothing is configured in main().
// api: ITK_AUTOLOAD_PATH is removed from the environment, then the autoload
// path is set with ObjectFactoryBase::SetAutoloadPath() before any
// factory is used.
//
// In both cases the factory library must be loaded lazily on first use.
int
main(int argc, char * argv[])
{
if (argc != 3)
{
std::cerr << "Usage: " << argv[0] << " <env|api> <FactoryLibraryDirectory>" << std::endl;
return EXIT_FAILURE;
}
const std::string mode{ argv[1] };
const std::string factoryDirectory{ argv[2] };

if (mode == "api")
{
// Ensure the setter, not an inherited environment, provides the path.
itksys::SystemTools::UnPutEnv("ITK_AUTOLOAD_PATH");
itk::ObjectFactoryBase::SetAutoloadPath(factoryDirectory);
}
else if (mode != "env")
{
std::cerr << "Unknown mode: " << mode << std::endl;
return EXIT_FAILURE;
}

if (itk::ObjectFactoryBase::GetAutoloadPath() != factoryDirectory)
{
std::cerr << "Autoload path is \"" << itk::ObjectFactoryBase::GetAutoloadPath() << "\", expected \""
<< factoryDirectory << '"' << std::endl;
return EXIT_FAILURE;
}

// Objects created by the dynamic factory must be released before ReHash() unloads its library.
{
const auto container = itk::ImportImageContainer<unsigned long, short>::New();
if (std::strcmp(container->GetNameOfClass(), "TestImportImageContainer") != 0)
{
std::cerr << "Dynamic factory was not loaded; created class: " << container->GetNameOfClass() << std::endl;
return EXIT_FAILURE;
}
}

// An empty path must not load the factory after a rehash.
itk::ObjectFactoryBase::SetAutoloadPath("");
itk::ObjectFactoryBase::ReHash();
if (std::strcmp(itk::ImportImageContainer<unsigned long, short>::New()->GetNameOfClass(),
"TestImportImageContainer") == 0)
{
std::cerr << "Dynamic factory was loaded with an empty autoload path." << std::endl;
return EXIT_FAILURE;
}

return EXIT_SUCCESS;
}
13 changes: 2 additions & 11 deletions Modules/Core/Common/test/itkObjectFactoryTest2.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -114,17 +114,8 @@ itkObjectFactoryTest2(int argc, char * argv[])
#ifdef CMAKE_INTDIR
path += std::string("/") + std::string(CMAKE_INTDIR);
#endif
const std::string itk_autoload_env{ "ITK_AUTOLOAD_PATH" };
const std::string myenv{ itk_autoload_env + "=" + path };
itksys::SystemTools::PutEnv(myenv);
std::cout << "SetValue => " << myenv << std::endl;
std::string getmyenv;
if (!itksys::SystemTools::GetEnv(itk_autoload_env, getmyenv))
{
std::cerr << "ERROR: Environmental variable not set as requested : " << itk_autoload_env << "!=" << path
<< std::endl;
}
std::cout << "GetValue => " << itk_autoload_env + "=" + getmyenv << std::endl;
itk::ObjectFactoryBase::SetAutoloadPath(path);
std::cout << "Autoload path => " << itk::ObjectFactoryBase::GetAutoloadPath() << std::endl;

itk::ObjectFactoryBase::ReHash();

Expand Down
Loading