diff --git a/docs/api/constants.rst b/docs/api/constants.rst index 2652b8a148..4d033ea8cb 100644 --- a/docs/api/constants.rst +++ b/docs/api/constants.rst @@ -47,6 +47,14 @@ These environmental variables are used by the OpenColorIO library Ex: OCIO_OPTIMIZATION_FLAGS="20479" or "0x4FFF" for OPTIMIZATION_LOSSLESS. + .. data:: PyOpenColorIO.OCIO_ARCHIVE_FLAGS_ENVVAR + + The envvar 'OCIO_ARCHIVE_FLAGS' provides a way to modify the parameters + used to create an ocioz archive. Remove the variable or set the value + to empty to not use it. Set the value of the variable to the desired + parameters as either an integer or hexadecimal value. + Ex: OCIO_ARCHIVE_FLAGS="256" or "0x0100" for ARCHIVE_FLAGS_MINIMAL. + .. group-tab:: C++ .. doxygengroup:: VarsEnvvar diff --git a/include/OpenColorIO/OpenColorIO.h b/include/OpenColorIO/OpenColorIO.h index 784cf8d2fd..e95577dac9 100644 --- a/include/OpenColorIO/OpenColorIO.h +++ b/include/OpenColorIO/OpenColorIO.h @@ -12,6 +12,7 @@ #include #include #include +#include #include "OpenColorABI.h" #include "OpenColorTypes.h" @@ -1503,7 +1504,7 @@ class OCIOEXPORT Config * * \return bool Archivable if true. */ - bool isArchivable() const; + bool isArchivable(bool minimal) const; /** * \brief Archive the config and its LUTs into the specified output stream. @@ -1521,6 +1522,8 @@ class OCIOEXPORT Config * trying to resolve all the FileTransforms in the Config to specific files is because of the * goal to allow context variables to continue to work. * + * Archiving a minimal Config will try resolve these file references using the current context. + * * If a Config is created with CreateFromStream, CreateFromFile with an OCIOZ archive, or * CreateFromConfigIOProxy, it cannot be archived unless the working directory is manually set * to a directory that contains any necessary LUT files. @@ -1528,8 +1531,12 @@ class OCIOEXPORT Config * The provided output stream must be closed by the caller, if necessary (e.g., an ofstream). * * \param ostream The output stream to write to. + * \param flags Flags top control archive creation */ - void archive(std::ostream & ostream) const; + void archive(std::ostream & ostream, ArchiveFlags flags) const; + + //TODO: document + void GetAllFileReferences(std::set & files) const; Config(const Config &) = delete; Config& operator= (const Config &) = delete; diff --git a/include/OpenColorIO/OpenColorTypes.h b/include/OpenColorIO/OpenColorTypes.h index 33654baf83..e24d92b240 100644 --- a/include/OpenColorIO/OpenColorTypes.h +++ b/include/OpenColorIO/OpenColorTypes.h @@ -764,6 +764,15 @@ extern OCIOEXPORT const char * OCIO_INACTIVE_COLORSPACES_ENVVAR; */ extern OCIOEXPORT const char * OCIO_OPTIMIZATION_FLAGS_ENVVAR; +/** + * The envvar 'OCIO_OPTIMIZATION_FLAGS' provides a way to force a given + * optimization level. Remove the variable or set the value to empty to + * not use it. Set the value of the variable to the desired optimization + * level as either an integer or hexadecimal value. + * Ex: OCIO_OPTIMIZATION_FLAGS="1" or "0x01" for ARCHIVE_FLAGS_MINIMAL. + */ +extern OCIOEXPORT const char * OCIO_ARCHIVE_FLAGS_ENVVAR; + /** * The envvar 'OCIO_USER_CATEGORIES' allows the end-user to filter color spaces shown by * applications. Only color spaces that include at least one of the supplied categories will be @@ -945,6 +954,28 @@ extern OCIOEXPORT const char * OCIO_CONFIG_DEFAULT_NAME; extern OCIOEXPORT const char * OCIO_CONFIG_DEFAULT_FILE_EXT; extern OCIOEXPORT const char * OCIO_CONFIG_ARCHIVE_FILE_EXT; +//!cpp:type:: Enum to control the behavior of archiving a :cpp:class:`Config`. +// +// TODO: extend docs +// TODO: Python bindings +// TODO: environment variable? +// +enum ArchiveFlags : unsigned long +{ + ARCHIVE_FLAGS_NONE = 0x0000, + + ARCHIVE_FLAGS_FAST_COMPRESSION = 0x02, // Compression level + ARCHIVE_FLAGS_NORMAL_COMPRESSION = 0x06, // Compression level + ARCHIVE_FLAGS_BEST_COMPRESSION = 0x09, // Compression level + ARCHIVE_FLAGS_COMPRESSION_MASK = 0x0F, // Compression level mask + + ARCHIVE_FLAGS_MINIMAL = 0x0100, // Generate a Minimal Archive + + ARCHIVE_FLAGS_DEFAULT = (ARCHIVE_FLAGS_NONE | ARCHIVE_FLAGS_BEST_COMPRESSION), + + ARCHIVE_FLAGS_SAFEST = (ARCHIVE_FLAGS_NONE | ARCHIVE_FLAGS_BEST_COMPRESSION) // TODO: name is poor +}; + // Built-in config feature // URI Prefix extern OCIOEXPORT const char * OCIO_BUILTIN_URI_PREFIX; diff --git a/src/OpenColorIO/Config.cpp b/src/OpenColorIO/Config.cpp index 03119d12ba..22a0e34774 100644 --- a/src/OpenColorIO/Config.cpp +++ b/src/OpenColorIO/Config.cpp @@ -49,6 +49,7 @@ const char * OCIO_ACTIVE_DISPLAYS_ENVVAR = "OCIO_ACTIVE_DISPLAYS"; const char * OCIO_ACTIVE_VIEWS_ENVVAR = "OCIO_ACTIVE_VIEWS"; const char * OCIO_INACTIVE_COLORSPACES_ENVVAR = "OCIO_INACTIVE_COLORSPACES"; const char * OCIO_OPTIMIZATION_FLAGS_ENVVAR = "OCIO_OPTIMIZATION_FLAGS"; +const char * OCIO_ARCHIVE_FLAGS_ENVVAR = "OCIO_ARCHIVE_FLAGS"; const char * OCIO_USER_CATEGORIES_ENVVAR = "OCIO_USER_CATEGORIES"; // Default filename (with extension) of a config and archived config. @@ -1103,6 +1104,16 @@ class Config::Impl // That should never happen. return -1; } + + void GetAllFileReferences(std::set & files) const + { + ConstTransformVec allTransforms; + this->getAllInternalTransforms(allTransforms); + for(const auto & transform : allTransforms) + { + GetFileReferences(files, transform); + } + } }; @@ -1956,14 +1967,8 @@ void Config::validate() const ///// Resolve all file Transforms using context variables. { - ConstTransformVec allTransforms; - getImpl()->getAllInternalTransforms(allTransforms); - std::set files; - for (const auto & transform : allTransforms) - { - GetFileReferences(files, transform); - } + getImpl()->GetAllFileReferences(files); // Check that at least one of the search paths can be resolved into a valid path. // Note that a search path without context variable(s) always correctly resolves. @@ -4890,14 +4895,8 @@ const char * Config::getCacheID(const ConstContextRcPtr & context) const { std::ostringstream filehash; - ConstTransformVec allTransforms; - getImpl()->getAllInternalTransforms(allTransforms); - std::set files; - for(const auto & transform : allTransforms) - { - GetFileReferences(files, transform); - } + getImpl()->GetAllFileReferences(files); for(const auto & iter : files) { @@ -5473,7 +5472,7 @@ ConfigIOProxyRcPtr Config::getConfigIOProxy() const return getImpl()->m_context->getConfigIOProxy(); } -bool Config::isArchivable() const +bool Config::isArchivable(bool minimal) const { ConstContextRcPtr context = getCurrentContext(); @@ -5486,7 +5485,7 @@ bool Config::isArchivable() const } // Utility lambda to check the following criteria. - auto validatePathForArchiving = [](const std::string & path) + auto validatePathForArchiving = [&minimal](const std::string & path) { // Using the normalized path. const std::string normPath = pystring::os::path::normpath(path); @@ -5496,9 +5495,10 @@ bool Config::isArchivable() const // 2) Path may not start with double dot ".." (going above working directory). pystring::startswith(normPath, "..") || // 3) A context variable may not be located at the start of the path. - (ContainsContextVariables(path) && - (StringUtils::Find(path, "$") == 0 || - StringUtils::Find(path, "%") == 0))) + (ContainsContextVariables(path) && //TODO: if we can resolve context, do so and validate path to file + (!minimal && + (StringUtils::Find(path, "$") == 0 || + StringUtils::Find(path, "%") == 0)))) { return false; } @@ -5525,14 +5525,8 @@ bool Config::isArchivable() const ///////////////////////////////// // FileTransform verification. // ///////////////////////////////// - ConstTransformVec allTransforms; - getImpl()->getAllInternalTransforms(allTransforms); - std::set files; - for(const auto & transform : allTransforms) - { - GetFileReferences(files, transform); - } + getImpl()->GetAllFileReferences(files); // Check that FileTransform sources are not absolute nor have context variables outside of // config working directory. @@ -5548,10 +5542,15 @@ bool Config::isArchivable() const return true; } -void Config::archive(std::ostream & ostream) const +void Config::archive(std::ostream & ostream, ArchiveFlags flags) const { // Using utility functions in OCIOZArchive.cpp. - archiveConfig(ostream, *this, getCurrentContext()->getWorkingDir()); + archiveConfig(ostream, *this, getCurrentContext()->getWorkingDir(), flags); +} + +void Config::GetAllFileReferences(std::set & files) const +{ + return getImpl()->GetAllFileReferences(files); } } // namespace OCIO_NAMESPACE diff --git a/src/OpenColorIO/OCIOZArchive.cpp b/src/OpenColorIO/OCIOZArchive.cpp index 982fce6823..2965ecaf21 100644 --- a/src/OpenColorIO/OCIOZArchive.cpp +++ b/src/OpenColorIO/OCIOZArchive.cpp @@ -16,6 +16,7 @@ #include "Platform.h" #include "utils/StringUtils.h" #include "transforms/FileTransform.h" +#include "Logging.h" #include "OCIOZArchive.h" @@ -183,6 +184,9 @@ void addSupportedFiles(void * archiver, const char * path, const char * configWo if (!possibleFormats.empty()) { // The extension is supported. Add the current file to the OCIOZ archive. + std::ostringstream logMessage; + logMessage << "Adding file: " << absPath; + LogInfo(logMessage.str()); if (mz_zip_writer_add_path( archiver, absPath.c_str(), configWorkingDirectory, 0, 1) != MZ_OK) @@ -200,9 +204,70 @@ void addSupportedFiles(void * archiver, const char * path, const char * configWo mz_os_close_dir(dir); } } + +ContextRcPtr addReferencedFiles(void * archiver, const ConfigRcPtr & config) +{ + ConstContextRcPtr context = config->getCurrentContext(); + ContextRcPtr ctxFilepath = Context::Create(); + ctxFilepath->setSearchPath(context->getSearchPath()); + ctxFilepath->setWorkingDir(context->getWorkingDir()); + ctxFilepath->setConfigIOProxy(context->getConfigIOProxy()); + + auto prefixLength = std::string(context->getWorkingDir()).length() + 1; // +1 add trailing '/' TODO: improve this + + std::set files; + config->GetAllFileReferences(files); + std::set added_files; + for (const auto &file : files) + { + const std::string resolvedPath = context->resolveFileLocation(file.c_str(), ctxFilepath); + const std::string relativePath = resolvedPath.substr(prefixLength); + std::ostringstream logMessage; + + if (added_files.find(relativePath) == added_files.end()) + { + logMessage << "Adding file: " << file << " -> " << relativePath; + LogInfo(logMessage.str()); + auto returnCode = mz_zip_writer_add_file(archiver, resolvedPath.c_str(), relativePath.c_str()); + if (returnCode != MZ_OK) + { + std::ostringstream os; + os << "Could not write file " << resolvedPath << " to in-memory archive.()" << returnCode << ")"; + throw Exception(os.str().c_str()); + } + } + else + { + logMessage << "Skipping already added file: " << file << " -> " << relativePath; + LogInfo(logMessage.str()); + } + added_files.insert(relativePath); + } + + const auto variableCount = ctxFilepath->getNumStringVars(); + for (auto i = 0; i != variableCount; ++i) + { + std::ostringstream logMessage; + logMessage << "Used Variable: " << ctxFilepath->getStringVarNameByIndex(i) << " -> " << ctxFilepath->getStringVarByIndex(i); + LogInfo(logMessage.str()); + } + return ctxFilepath; +} + ////////////////////////////////////////////////////////////////////////////////////// -void archiveConfig(std::ostream & ostream, const Config & config, const char * configWorkingDirectory) +ArchiveFlags EnvironmentOverride(ArchiveFlags oFlags) // TODO: test override +{ + const std::string envFlag = GetEnvVariable(OCIO_ARCHIVE_FLAGS_ENVVAR); + if (!envFlag.empty()) + { + // Use 0 to allow base to be determined by the format. + oFlags = static_cast(std::stoul(envFlag, nullptr, 0)); + } + return oFlags; +} + +void archiveConfig(std::ostream & ostream, const Config & config, const char * configWorkingDirectory, ArchiveFlags flags) { void * archiver = nullptr; void *write_mem_stream = NULL; @@ -210,7 +275,10 @@ void archiveConfig(std::ostream & ostream, const Config & config, const char * c int32_t buffer_size = 0; mz_zip_file file_info; - if (!config.isArchivable()) + flags = EnvironmentOverride(flags); + const bool minimal = HasFlag(flags, ARCHIVE_FLAGS_MINIMAL); + + if (!config.isArchivable(minimal)) // TODO: pass in flags? { std::ostringstream os; os << "Config is not archivable."; @@ -220,11 +288,6 @@ void archiveConfig(std::ostream & ostream, const Config & config, const char * c // Initialize. memset(&file_info, 0, sizeof(file_info)); - // Retrieve and store the config as string. - std::stringstream ss; - config.serialize(ss); - std::string configStr = ss.str(); - // Write zip to memory stream. #if MZ_VERSION_BUILD >= 040000 write_mem_stream = mz_stream_mem_create(); @@ -238,8 +301,10 @@ void archiveConfig(std::ostream & ostream, const Config & config, const char * c ArchiveOptions options; // Make sure that the compression method is set to DEFLATE. options.compress_method = ArchiveCompressionMethods::DEFLATE; - // Make sure that the compression level is set to BEST. - options.compress_level = ArchiveCompressionLevels::BEST; + // Default compression level is set to BEST. + options.compress_level = flags & ARCHIVE_FLAGS_COMPRESSION_MASK + ? ArchiveCompressionLevels(flags & ARCHIVE_FLAGS_COMPRESSION_MASK) + : ArchiveCompressionLevels::BEST; // Create the writer handle. #if MZ_VERSION_BUILD >= 040000 @@ -260,10 +325,38 @@ void archiveConfig(std::ostream & ostream, const Config & config, const char * c // Open the in-memory zip. if (mz_zip_writer_open(archiver, write_mem_stream, 0) == MZ_OK) { + ConfigRcPtr editiableConfig = config.createEditableCopy(); // TODO: is this a little heavy handed just so we can modify the envronment? + /////////////////////// + // Adding LUT files // + /////////////////////// + if (minimal) + { + ContextRcPtr archivedContext = addReferencedFiles(archiver, editiableConfig); + + // Need to make sure the used evironment context is in the config + const auto variableCount = archivedContext->getNumStringVars(); + for (auto i = 0; i != variableCount; ++i) + { + editiableConfig->addEnvironmentVar(archivedContext->getStringVarNameByIndex(i), archivedContext->getStringVarByIndex(i)); + } + } + else + { + // Add all supported files to in-memory zip from any directories under working directory. + // (recursive) + addSupportedFiles(archiver, configWorkingDirectory, configWorkingDirectory); + } + + ////////////////////////////// + // Adding config to archive // + ////////////////////////////// // Use a hardcoded name for the config's filename inside the archive. std::string configFullname = std::string(OCIO_CONFIG_DEFAULT_NAME) + std::string(OCIO_CONFIG_DEFAULT_FILE_EXT); + std::stringstream ss; + editiableConfig->serialize(ss); + std::string configStr = ss.str(); // Get config string size. int32_t configSize = (int32_t) configStr.size(); @@ -273,11 +366,13 @@ void archiveConfig(std::ostream & ostream, const Config & config, const char * c file_info.version_madeby = MZ_VERSION_MADEBY; file_info.compression_method = MZ_COMPRESS_METHOD_DEFLATE; file_info.flag = MZ_ZIP_FLAG_UTF8; - file_info.uncompressed_size = configSize; + file_info.uncompressed_size = configSize; // Retrieve and store the config as string. + + constexpr uint32_t posix_attrib = 0100644; // File with -rw-r--r-- permissions + constexpr uint32_t msdos_attrib = 0200; // MSDOS equivalent + file_info.external_fa = msdos_attrib; + file_info.external_fa |= (posix_attrib << 16); - ////////////////////////////// - // Adding config to archive // - ////////////////////////////// int32_t written = 0; // Opens an entry in the in-memory zip file for writing. if (mz_zip_writer_entry_open(archiver, &file_info) == MZ_OK) @@ -300,12 +395,9 @@ void archiveConfig(std::ostream & ostream, const Config & config, const char * c throw Exception(os.str().c_str()); } - /////////////////////// - // Adding LUT files // - /////////////////////// - // Add all supported files to in-memory zip from any directories under working directory. - // (recursive) - addSupportedFiles(archiver, configWorkingDirectory, configWorkingDirectory); + std::ostringstream comment; + comment << "Configuration written by archiveConfig() OCIO: " << GetVersion(); + mz_zip_writer_set_comment(archiver, comment.str().c_str()); // Close in-memory zip. mz_zip_writer_close(archiver); diff --git a/src/OpenColorIO/OCIOZArchive.h b/src/OpenColorIO/OCIOZArchive.h index 5bdbc6315b..fc850d4653 100644 --- a/src/OpenColorIO/OCIOZArchive.h +++ b/src/OpenColorIO/OCIOZArchive.h @@ -23,11 +23,13 @@ namespace OCIO_NAMESPACE * \param ostream Output stream to write the data into. * \param config Config object. * \param configWorkingDirectory Working directory of the current config. + * \param flags Archive flags used to . */ void archiveConfig( std::ostream & ostream, const Config & config, - const char * configWorkingDirectory); + const char * configWorkingDirectory, + ArchiveFlags flags); /** * \brief Get the content of a file inside an OCIOZ archive as a buffer. @@ -106,6 +108,11 @@ class CIOPOciozArchive : public ConfigIOProxy std::map m_entries; }; +// TODO: This "duplicates" flags function from Op.h could be a template? +inline bool HasFlag(ArchiveFlags flags, ArchiveFlags queryFlag) +{ + return (flags & queryFlag) == queryFlag; +} } // namespace OCIO_NAMESPACE #endif diff --git a/src/apps/ocioarchive/main.cpp b/src/apps/ocioarchive/main.cpp index 68054a6daf..b9bfc1ba19 100644 --- a/src/apps/ocioarchive/main.cpp +++ b/src/apps/ocioarchive/main.cpp @@ -45,6 +45,7 @@ int main(int argc, const char **argv) // Default value is current directory. std::string extractDestination; + bool minimal = false; bool extract = false; bool list = false; bool help = false; @@ -61,6 +62,9 @@ int main(int argc, const char **argv) " ocioarchive myarchive\n\n" " # Archive myconfig/config.ocio into myarchive.ocioz\n" " ocioarchive myarchive --iconfig myconfig/config.ocio\n\n" + " # Archive from the OCIO environment variable into myarchive.ocioz\n" + " # requires all environment variables to be resolvable\n" + " ocioarchive myarchive --minimal\n\n" " # Extract myarchive.ocioz into new directory named myarchive\n" " ocioarchive --extract myarchive.ocioz\n\n" " # Extract myarchive.ocioz into new directory named ocio_config\n" @@ -70,6 +74,7 @@ int main(int argc, const char **argv) "%*", parse_end_args, "", "", "Options:", "--iconfig %s", &configFilename, "Config to archive (takes precedence over $OCIO)", + "--minimal", &minimal, "Create a minimal archive", "--extract", &extract, "Extract an OCIOZ config archive", "--dir %s", &extractDestination, "Path where to extract the files (folders are created if missing)", "--list", &list, "List the files inside an archive without extracting it", @@ -118,7 +123,7 @@ int main(int argc, const char **argv) std::cerr << "ERROR: Could not load config: " << configFilename << std::endl; exit(1); } - + } else if (ocioEnv && *ocioEnv) { @@ -131,7 +136,7 @@ int main(int argc, const char **argv) catch (...) { // Capture any errors and display a custom message. - std::cerr << "ERROR: Could not load config from $OCIO variable: " + std::cerr << "ERROR: Could not load config from $OCIO variable: " << ocioEnv << std::endl; exit(1); } @@ -142,11 +147,11 @@ int main(int argc, const char **argv) exit(1); } - try + try { // The ocioz extension is added by the archive method. The assumption is that // archiveName is the filename without extension. - + // Do not add ocioz extension if already present. if (!StringUtils::EndsWith(archiveName, ".ocioz")) { @@ -156,12 +161,16 @@ int main(int argc, const char **argv) std::ofstream ofstream(archiveName, std::ofstream::out | std::ofstream::binary); if (ofstream.good()) { - config->archive(ofstream); + OCIO::ArchiveFlags flags = OCIO::ARCHIVE_FLAGS_DEFAULT; + if (minimal) + flags = OCIO::ArchiveFlags(flags | OCIO::ARCHIVE_FLAGS_MINIMAL); + + config->archive(ofstream, flags); ofstream.close(); } else { - std::cerr << "Could not open output stream for: " + std::cerr << "Could not open output stream for: " << archiveName + std::string(OCIO::OCIO_CONFIG_ARCHIVE_FILE_EXT) << std::endl; exit(1); @@ -171,13 +180,13 @@ int main(int argc, const char **argv) { std::cerr << e.what() << std::endl; exit(1); - } + } } catch (OCIO::Exception & exception) { std::cerr << "ERROR: " << exception.what() << std::endl; exit(1); - } + } catch (std::exception& exception) { std::cerr << "ERROR: " << exception.what() << std::endl; @@ -201,7 +210,7 @@ int main(int argc, const char **argv) } archiveName = args[0].c_str(); - try + try { if (extractDestination.empty()) { @@ -241,7 +250,7 @@ int main(int argc, const char **argv) mz_zip_reader_create(&reader); #endif struct tm tmu_date; - + err = mz_zip_reader_open_file(reader, path.c_str()); if (err != MZ_OK) { @@ -264,7 +273,7 @@ int main(int argc, const char **argv) err = mz_zip_reader_entry_get_info(reader, &file_info); if (err != MZ_OK) { - std::cerr << "ERROR: Could not get information from entry: " << file_info->filename + std::cerr << "ERROR: Could not get information from entry: " << file_info->filename << std::endl; exit(1); } @@ -281,6 +290,13 @@ int main(int argc, const char **argv) err = mz_zip_reader_goto_next_entry(reader); } while (err == MZ_OK); + + const char *global_comment = nullptr; + if (mz_zip_reader_get_comment(reader, &global_comment) == MZ_OK) + { + if (global_comment) + std::cout << "\n" << global_comment << "\n"; + } } // Generic error handling. diff --git a/src/apps/ociocheck/main.cpp b/src/apps/ociocheck/main.cpp index b3c8c33854..acf8a1e666 100644 --- a/src/apps/ociocheck/main.cpp +++ b/src/apps/ociocheck/main.cpp @@ -504,6 +504,7 @@ int main(int argc, const char **argv) std::string cacheID; bool isArchivable = false; + const bool minimal = false; try { LogGuard logGuard; @@ -512,7 +513,7 @@ int main(int argc, const char **argv) std::cout << logGuard.output(); cacheID = config->getCacheID(); - isArchivable = config->isArchivable(); + isArchivable = config->isArchivable(minimal); // Passed if there are no Error level logs. StringUtils::StringVec svec = StringUtils::SplitByLines(logGuard.output()); diff --git a/src/apps/ocioview/main.py b/src/apps/ocioview/main.py index 609ef73d8d..8b12f2025b 100644 --- a/src/apps/ocioview/main.py +++ b/src/apps/ocioview/main.py @@ -56,6 +56,7 @@ def excepthook(exc_type, exc_value, exc_tb): ocio.OCIO_ACTIVE_DISPLAYS_ENVVAR, ocio.OCIO_INACTIVE_COLORSPACES_ENVVAR, ocio.OCIO_OPTIMIZATION_FLAGS_ENVVAR, + ocio.OCIO_ARCHIVE_FLAGS_ENVVAR, ocio.OCIO_USER_CATEGORIES_ENVVAR, ): if env_var in os.environ: diff --git a/src/bindings/python/PyConfig.cpp b/src/bindings/python/PyConfig.cpp index 675f7155ca..b4a6bf9a2b 100644 --- a/src/bindings/python/PyConfig.cpp +++ b/src/bindings/python/PyConfig.cpp @@ -890,13 +890,18 @@ void bindPyConfig(py::module & m) DOC(Config, setProcessorCacheFlags)) // Archiving - .def("isArchivable", &Config::isArchivable, DOC(Config, isArchivable)) + .def("isArchivable", [](ConfigRcPtr & self, bool minimal) + { + return self->isArchivable(minimal); + }, + py::arg("minimal") = false, + DOC(Config, isArchivable)) .def("archive", [](ConfigRcPtr & self, const char * filepath) { std::ofstream f(filepath, std::ofstream::out | std::ofstream::binary); - self->archive(f); + self->archive(f, ARCHIVE_FLAGS_DEFAULT); // TODO: pass flags in rather than default f.close(); - }, + }, DOC(Config, archive)) // Conversion to string diff --git a/src/bindings/python/PyTypes.cpp b/src/bindings/python/PyTypes.cpp index 760d715587..6085d0a242 100644 --- a/src/bindings/python/PyTypes.cpp +++ b/src/bindings/python/PyTypes.cpp @@ -851,6 +851,7 @@ void bindPyTypes(py::module & m) m.attr("OCIO_ACTIVE_VIEWS_ENVVAR") = OCIO_ACTIVE_VIEWS_ENVVAR; m.attr("OCIO_INACTIVE_COLORSPACES_ENVVAR") = OCIO_INACTIVE_COLORSPACES_ENVVAR; m.attr("OCIO_OPTIMIZATION_FLAGS_ENVVAR") = OCIO_OPTIMIZATION_FLAGS_ENVVAR; + m.attr("OCIO_ARCHIVE_FLAGS_ENVVAR") = OCIO_ARCHIVE_FLAGS_ENVVAR; m.attr("OCIO_USER_CATEGORIES_ENVVAR") = OCIO_USER_CATEGORIES_ENVVAR; // Roles diff --git a/tests/cpu/OCIOZArchive_tests.cpp b/tests/cpu/OCIOZArchive_tests.cpp index 4906618b31..ebca97de60 100644 --- a/tests/cpu/OCIOZArchive_tests.cpp +++ b/tests/cpu/OCIOZArchive_tests.cpp @@ -73,6 +73,7 @@ OCIO_ADD_TEST(OCIOZArchive, is_config_archivable) std::istringstream iss; iss.str(CONFIG); + const bool minimal = false; OCIO::ConfigRcPtr cfg; OCIO_CHECK_NO_THROW(cfg = OCIO::Config::CreateFromStream(iss)->createEditableCopy()); @@ -95,39 +96,39 @@ OCIO_ADD_TEST(OCIOZArchive, is_config_archivable) // Valid search path. cfg->setSearchPath("luts"); - OCIO_CHECK_EQUAL(true, cfg->isArchivable()); + OCIO_CHECK_EQUAL(true, cfg->isArchivable(minimal)); cfg->setSearchPath(R"(luts/myluts1)"); - OCIO_CHECK_EQUAL(true, cfg->isArchivable()); + OCIO_CHECK_EQUAL(true, cfg->isArchivable(minimal)); cfg->setSearchPath(R"(luts\myluts1)"); - OCIO_CHECK_EQUAL(true, cfg->isArchivable()); + OCIO_CHECK_EQUAL(true, cfg->isArchivable(minimal)); // Valid Search path starting with "./" or ".\". cfg->setSearchPath(R"(./myLuts)"); - OCIO_CHECK_EQUAL(true, cfg->isArchivable()); + OCIO_CHECK_EQUAL(true, cfg->isArchivable(minimal)); cfg->setSearchPath(R"(.\myLuts)"); - OCIO_CHECK_EQUAL(true, cfg->isArchivable()); + OCIO_CHECK_EQUAL(true, cfg->isArchivable(minimal)); // Valid search path starting with "./" or ".\" and a context variable. cfg->setSearchPath(R"(./$SHOT/myluts)"); - OCIO_CHECK_EQUAL(true, cfg->isArchivable()); + OCIO_CHECK_EQUAL(true, cfg->isArchivable(minimal)); cfg->setSearchPath(R"(.\$SHOT\myluts)"); - OCIO_CHECK_EQUAL(true, cfg->isArchivable()); + OCIO_CHECK_EQUAL(true, cfg->isArchivable(minimal)); cfg->setSearchPath(R"(luts/$SHOT)"); - OCIO_CHECK_EQUAL(true, cfg->isArchivable()); + OCIO_CHECK_EQUAL(true, cfg->isArchivable(minimal)); cfg->setSearchPath(R"(luts/$SHOT/luts1)"); - OCIO_CHECK_EQUAL(true, cfg->isArchivable()); + OCIO_CHECK_EQUAL(true, cfg->isArchivable(minimal)); cfg->setSearchPath(R"(luts\$SHOT)"); - OCIO_CHECK_EQUAL(true, cfg->isArchivable()); + OCIO_CHECK_EQUAL(true, cfg->isArchivable(minimal)); cfg->setSearchPath(R"(luts\$SHOT\luts1)"); - OCIO_CHECK_EQUAL(true, cfg->isArchivable()); + OCIO_CHECK_EQUAL(true, cfg->isArchivable(minimal)); /* * Illegal scenarios @@ -135,34 +136,34 @@ OCIO_ADD_TEST(OCIOZArchive, is_config_archivable) // Illegal search path starting with "..". cfg->setSearchPath(R"(luts:../luts)"); - OCIO_CHECK_EQUAL(false, cfg->isArchivable()); + OCIO_CHECK_EQUAL(false, cfg->isArchivable(minimal)); cfg->setSearchPath(R"(luts:..\myLuts)"); - OCIO_CHECK_EQUAL(false, cfg->isArchivable()); + OCIO_CHECK_EQUAL(false, cfg->isArchivable(minimal)); // Illegal search path starting with a context variable. cfg->setSearchPath(R"(luts:$SHOT)"); - OCIO_CHECK_EQUAL(false, cfg->isArchivable()); + OCIO_CHECK_EQUAL(false, cfg->isArchivable(minimal)); // Illegal search path with absolute path. cfg->setSearchPath(R"(luts:/luts)"); - OCIO_CHECK_EQUAL(false, cfg->isArchivable()); + OCIO_CHECK_EQUAL(false, cfg->isArchivable(minimal)); cfg->setSearchPath(R"(luts:/$SHOT)"); - OCIO_CHECK_EQUAL(false, cfg->isArchivable()); + OCIO_CHECK_EQUAL(false, cfg->isArchivable(minimal)); #ifdef _WIN32 cfg->clearSearchPaths(); cfg->addSearchPath(R"(C:\luts)"); - OCIO_CHECK_EQUAL(false, cfg->isArchivable()); + OCIO_CHECK_EQUAL(false, cfg->isArchivable(minimal)); cfg->clearSearchPaths(); cfg->addSearchPath(R"(C:\)"); - OCIO_CHECK_EQUAL(false, cfg->isArchivable()); + OCIO_CHECK_EQUAL(false, cfg->isArchivable(minimal)); cfg->clearSearchPaths(); cfg->addSearchPath(R"(C:\$SHOT)"); - OCIO_CHECK_EQUAL(false, cfg->isArchivable()); + OCIO_CHECK_EQUAL(false, cfg->isArchivable(minimal)); #endif } @@ -170,7 +171,7 @@ OCIO_ADD_TEST(OCIOZArchive, is_config_archivable) cfg->clearSearchPaths(); // Lambda function to facilitate adding a new FileTransform to a config. - auto addFTAndTestIsArchivable = [&cfg](const std::string & path, bool isArchivable) + auto addFTAndTestIsArchivable = [&cfg](const std::string & path, bool isArchivable, bool minimal) { std::string fullPath = pystring::os::path::join(path, "fake_lut.clf"); auto ft = OCIO::FileTransform::Create(); @@ -181,7 +182,7 @@ OCIO_ADD_TEST(OCIOZArchive, is_config_archivable) cs->setTransform(ft, OCIO::COLORSPACE_DIR_TO_REFERENCE); cfg->addColorSpace(cs); - OCIO_CHECK_EQUAL(isArchivable, cfg->isArchivable()); + OCIO_CHECK_EQUAL(isArchivable, cfg->isArchivable(minimal)); cfg->removeColorSpace("csTest"); }; @@ -193,41 +194,41 @@ OCIO_ADD_TEST(OCIOZArchive, is_config_archivable) */ // Valid FileTransform path. - addFTAndTestIsArchivable("luts", true); - addFTAndTestIsArchivable(R"(luts/myluts1)", true); - addFTAndTestIsArchivable(R"(luts\myluts1)", true); + addFTAndTestIsArchivable("luts", true, false); + addFTAndTestIsArchivable(R"(luts/myluts1)", true, false); + addFTAndTestIsArchivable(R"(luts\myluts1)", true, false); // Valid Search path starting with "./" or ".\". - addFTAndTestIsArchivable(R"(./myLuts)", true); - addFTAndTestIsArchivable(R"(.\myLuts)", true); + addFTAndTestIsArchivable(R"(./myLuts)", true, false); + addFTAndTestIsArchivable(R"(.\myLuts)", true, false); // Valid search path starting with "./" or ".\" and a context variable. - addFTAndTestIsArchivable(R"(./$SHOT/myluts)", true); - addFTAndTestIsArchivable(R"(.\$SHOT\myluts)", true); - addFTAndTestIsArchivable(R"(luts/$SHOT)", true); - addFTAndTestIsArchivable(R"(luts/$SHOT/luts1)", true); - addFTAndTestIsArchivable(R"(luts\$SHOT)", true); - addFTAndTestIsArchivable(R"(luts\$SHOT\luts1)", true); + addFTAndTestIsArchivable(R"(./$SHOT/myluts)", true, false); + addFTAndTestIsArchivable(R"(.\$SHOT\myluts)", true, false); + addFTAndTestIsArchivable(R"(luts/$SHOT)", true, false); + addFTAndTestIsArchivable(R"(luts/$SHOT/luts1)", true, false); + addFTAndTestIsArchivable(R"(luts\$SHOT)", true, false); + addFTAndTestIsArchivable(R"(luts\$SHOT\luts1)", true, false); /* * Illegal scenarios */ // Illegal search path starting with "..". - addFTAndTestIsArchivable(R"(../luts)", false); - addFTAndTestIsArchivable(R"(..\myLuts)", false); + addFTAndTestIsArchivable(R"(../luts)", false, false); + addFTAndTestIsArchivable(R"(..\myLuts)", false, false); // Illegal search path starting with a context variable. - addFTAndTestIsArchivable(R"($SHOT)", false); + addFTAndTestIsArchivable(R"($SHOT)", false, false); // Illegal search path with absolute path. - addFTAndTestIsArchivable(R"(/luts)", false); - addFTAndTestIsArchivable(R"(/$SHOT)", false); + addFTAndTestIsArchivable(R"(/luts)", false, false); + addFTAndTestIsArchivable(R"(/$SHOT)", false, false); #ifdef _WIN32 - addFTAndTestIsArchivable(R"(C:\luts)", false); - addFTAndTestIsArchivable(R"(C:\)", false); - addFTAndTestIsArchivable(R"(\$SHOT)", false); + addFTAndTestIsArchivable(R"(C:\luts)", false, false); + addFTAndTestIsArchivable(R"(C:\)", false, false); + addFTAndTestIsArchivable(R"(\$SHOT)", false, false); #endif } } @@ -432,7 +433,7 @@ OCIO_ADD_TEST(OCIOZArchive, archive_config_and_compare_to_original) // 2 - Archive the config of step 1. std::ostringstream ostringStream; - OCIO_CHECK_NO_THROW(configFromOcioFile->archive(ostringStream)); + OCIO_CHECK_NO_THROW(configFromOcioFile->archive(ostringStream, OCIO::ARCHIVE_FLAGS_DEFAULT)); // 3 - Verify that the binary data starts with "PK". OCIO_CHECK_EQUAL('P', ostringStream.str()[0]); diff --git a/tests/python/ConstantsTest.py b/tests/python/ConstantsTest.py index 852d6a9072..95ededbac9 100644 --- a/tests/python/ConstantsTest.py +++ b/tests/python/ConstantsTest.py @@ -16,6 +16,7 @@ def test_string_constants(self): self.assertEqual(OCIO.OCIO_ACTIVE_VIEWS_ENVVAR, 'OCIO_ACTIVE_VIEWS') self.assertEqual(OCIO.OCIO_INACTIVE_COLORSPACES_ENVVAR, 'OCIO_INACTIVE_COLORSPACES') self.assertEqual(OCIO.OCIO_OPTIMIZATION_FLAGS_ENVVAR, 'OCIO_OPTIMIZATION_FLAGS') + self.assertEqual(OCIO.OCIO_ARCHIVE_FLAGS_ENVVAR, 'OCIO_ARCHIVE_FLAGS') self.assertEqual(OCIO.OCIO_USER_CATEGORIES_ENVVAR, 'OCIO_USER_CATEGORIES') # Cache (env. variables).