diff --git a/include/OpenColorIO/OpenColorIO.h b/include/OpenColorIO/OpenColorIO.h index f626c26cc8..e95577dac9 100644 --- a/include/OpenColorIO/OpenColorIO.h +++ b/include/OpenColorIO/OpenColorIO.h @@ -1504,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. diff --git a/src/OpenColorIO/Config.cpp b/src/OpenColorIO/Config.cpp index 578c8dd054..22a0e34774 100644 --- a/src/OpenColorIO/Config.cpp +++ b/src/OpenColorIO/Config.cpp @@ -5472,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(); @@ -5485,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,8 +5496,9 @@ bool Config::isArchivable() const pystring::startswith(normPath, "..") || // 3) A context variable may not be located at the start of the path. (ContainsContextVariables(path) && //TODO: if we can resolve context, do so and validate path to file - (StringUtils::Find(path, "$") == 0 || - StringUtils::Find(path, "%") == 0))) + (!minimal && + (StringUtils::Find(path, "$") == 0 || + StringUtils::Find(path, "%") == 0)))) { return false; } diff --git a/src/OpenColorIO/OCIOZArchive.cpp b/src/OpenColorIO/OCIOZArchive.cpp index 8d6c0b544e..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) @@ -201,9 +205,9 @@ void addSupportedFiles(void * archiver, const char * path, const char * configWo } } -void addReferencedFiles(void * archiver, const Config & config) +ContextRcPtr addReferencedFiles(void * archiver, const ConfigRcPtr & config) { - ConstContextRcPtr context = config.getCurrentContext(); + ConstContextRcPtr context = config->getCurrentContext(); ContextRcPtr ctxFilepath = Context::Create(); ctxFilepath->setSearchPath(context->getSearchPath()); ctxFilepath->setWorkingDir(context->getWorkingDir()); @@ -212,20 +216,42 @@ void addReferencedFiles(void * archiver, const Config & config) auto prefixLength = std::string(context->getWorkingDir()).length() + 1; // +1 add trailing '/' TODO: improve this std::set files; - config.GetAllFileReferences(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; - auto returnCode = mz_zip_writer_add_file(archiver, resolvedPath.c_str(), relativePath.c_str()); - if (returnCode != MZ_OK) + if (added_files.find(relativePath) == added_files.end()) { - std::ostringstream os; - os << "Could not write file " << resolvedPath << " to in-memory archive.()" << returnCode << ")"; - throw Exception(os.str().c_str()); + 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; } ////////////////////////////////////////////////////////////////////////////////////// @@ -250,8 +276,9 @@ void archiveConfig(std::ostream & ostream, const Config & config, const char * c mz_zip_file file_info; flags = EnvironmentOverride(flags); + const bool minimal = HasFlag(flags, ARCHIVE_FLAGS_MINIMAL); - if (!config.isArchivable()) // TODO: pass in flags? + if (!config.isArchivable(minimal)) // TODO: pass in flags? { std::ostringstream os; os << "Config is not archivable."; @@ -261,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(); @@ -303,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(); @@ -316,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) @@ -343,16 +395,6 @@ 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) - if (HasFlag(flags, ARCHIVE_FLAGS_MINIMAL)) - addReferencedFiles(archiver, config); - else - addSupportedFiles(archiver, configWorkingDirectory, configWorkingDirectory); - std::ostringstream comment; comment << "Configuration written by archiveConfig() OCIO: " << GetVersion(); mz_zip_writer_set_comment(archiver, comment.str().c_str()); 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/tests/cpu/OCIOZArchive_tests.cpp b/tests/cpu/OCIOZArchive_tests.cpp index 9d71e2edb0..e019b0e182 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 } @@ -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"); };