Skip to content

Commit

Permalink
Merge pull request #949 from Autodesk/kxl-adsk/better_version_check_c…
Browse files Browse the repository at this point in the history
…ontrol_for_plugins

Improved MAYA_PXR_PLUGINPATH_NAME to handle more checks
  • Loading branch information
Krystian Ligenza authored Dec 1, 2020
2 parents 8b0b0b0 + 892515b commit 2700caf
Show file tree
Hide file tree
Showing 22 changed files with 809 additions and 36 deletions.
7 changes: 7 additions & 0 deletions lib/mayaUsd/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,13 @@ if(UFE_FOUND AND MSVC)
)
endif()

target_compile_definitions(${PROJECT_NAME}
PRIVATE
MAYA_USD_VERSION=${USD_VERSION}
MAYAUSD_VERSION=${MAYAUSD_VERSION}
MAYA_PY_VERSION=${MAYA_PY_VERSION}
)

mayaUsd_compile_config(${PROJECT_NAME})

# -----------------------------------------------------------------------------
Expand Down
2 changes: 2 additions & 0 deletions lib/mayaUsd/base/debugCodes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ TF_REGISTRY_FUNCTION(TfDebug)
TF_DEBUG_ENVIRONMENT_SYMBOL(USDMAYA_PROXYSHAPEBASE, "Base proxy shape evaluation");
TF_DEBUG_ENVIRONMENT_SYMBOL(
USDMAYA_PROXYACCESSOR, "Debugging of the evaluation for mixed data models.");
TF_DEBUG_ENVIRONMENT_SYMBOL(
USDMAYA_PLUG_INFO_VERSION, "Debugging of the mayaUsd plug info version check.");
}

PXR_NAMESPACE_CLOSE_SCOPE
1 change: 1 addition & 0 deletions lib/mayaUsd/base/debugCodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ TF_DEBUG_CODES(
PXRUSDMAYA_TRANSLATORS,
USDMAYA_PROXYSHAPEBASE,
USDMAYA_PROXYACCESSOR,
USDMAYA_PLUG_INFO_VERSION,
USDMAYA_UNDOSTACK,
USDMAYA_UNDOSTATEDELEGATE);

Expand Down
1 change: 1 addition & 0 deletions lib/mayaUsd/utils/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ target_sources(${PROJECT_NAME}
converter.cpp
diagnosticDelegate.cpp
query.cpp
plugRegistryHelper.cpp
stageCache.cpp
undoHelperCommand
util.cpp
Expand Down
312 changes: 312 additions & 0 deletions lib/mayaUsd/utils/plugRegistryHelper.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,312 @@
//
// Copyright 2020 Autodesk
//
// 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
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// 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 "plugRegistryHelper.h"

#include <mayaUsd/base/debugCodes.h>

#include <pxr/base/js/json.h>
#include <pxr/base/js/value.h>
#include <pxr/base/plug/registry.h>
#include <pxr/base/tf/getenv.h>
#include <pxr/base/tf/pathUtils.h>
#include <pxr/base/tf/staticTokens.h>
#include <pxr/base/tf/stringUtils.h>

#include <fstream>
#include <string>

#if !defined(MAYAUSD_VERSION)
#error "MAYAUSD_VERSION is not defined"
#endif

#if !defined(MAYA_PY_VERSION)
#error "MAYA_PY_VERSION is not defined"
#endif

#define STRINGIFY(x) #x
#define TOSTRING(x) STRINGIFY(x)

PXR_NAMESPACE_USING_DIRECTIVE

namespace {

// clang-format off
TF_DEFINE_PRIVATE_TOKENS(_Tokens,
// Filename tokens
((MayaUsdPlugInfoName, "mayaUsdPlugInfo.json"))

// Top level key
((IncludesKey, "MayaUsdIncludes"))

// Include keys
((PlugPathKey, "PlugPath"))
((VersionCheckKey, "VersionCheck"))

// VersionCheck keys
((CheckPythonKey, "Python"))
((CheckUsdKey, "USD"))
((CheckMayaUsdKey, "MayaUsd"))
);
// clang-format on

/*! \brief Read the mayaUsd plug info in pathname into result.
\note Heavily inspired by /pxr/base/plug/info.cpp
\return true if the file could be opened.
*/
bool readPlugInfoObject(const std::string& pathname, JsObject& result)
{
result.clear();

// The file may not exist or be readable.
std::ifstream ifs;
ifs.open(pathname.c_str());
if (!ifs.is_open()) {
TF_RUNTIME_ERROR("Plugin info file %s couldn't be read", pathname.c_str());
return false;
}

// The Js library doesn't allow comments, but we'd like to allow them.
// Strip comments, retaining empty lines so line numbers reported in parse
// errors match line numbers in the original file content.
// NOTE: Joining a vector of strings and calling JsParseString()
// is *much* faster than writing to a stringstream and
// calling JsParseStream() as of this writing.
std::string line;
std::vector<std::string> filtered;
while (getline(ifs, line)) {
if (line.find('#') < line.find_first_not_of(" \t#"))
line.clear();
filtered.push_back(line);
}

// Read JSON.
JsParseError error;
JsValue plugInfo = JsParseString(TfStringJoin(filtered, "\n"), &error);

// Validate.
if (plugInfo.IsNull()) {
TF_RUNTIME_ERROR(
"Plugin info file %s couldn't be read "
"(line %d, col %d): %s",
pathname.c_str(),
error.line,
error.column,
error.reason.c_str());
} else if (!plugInfo.IsObject()) {
// The contents didn't evaluate to a json object....
TF_RUNTIME_ERROR("Plugin info file %s did not contain a JSON object", pathname.c_str());
} else {
result = plugInfo.GetJsObject();
}
return true;
}

/*! \brief Perform version check for given MayaUsd plug info
\return true if requested version are valid for current MayaUsd runtime configuration
*/
bool checkPluginVersions(
const JsObject& plugInfo,
const std::string& pythonVersion,
const std::string& usdVersion,
const std::string& mayaUsdVersion,
const std::string& debugLocation)
{
JsObject::const_iterator checkIt = plugInfo.find(_Tokens->VersionCheckKey);
if (checkIt == plugInfo.end()) {
// Version check wasn't requested
return true;
}

if (!checkIt->second.IsObject()) {
TF_RUNTIME_ERROR(
"Plugin info %s key '%s' doesn't hold an object",
debugLocation.c_str(),
checkIt->first.c_str());
return false;
}

JsObject versionCheckObject = checkIt->second.GetJsObject();

auto checkFn = [&versionCheckObject,
&debugLocation](const std::string& key, const std::string& versionValue) {
JsObject::const_iterator checkIt = versionCheckObject.find(key);
if (checkIt == versionCheckObject.end()) {
// version check for this key was not requested
TF_DEBUG(USDMAYA_PLUG_INFO_VERSION)
.Msg(
"Plugin info %s version check '%s' not requested\n",
debugLocation.c_str(),
key.c_str());
return true;
}

if (!checkIt->second.IsString()) {
TF_RUNTIME_ERROR(
"Plugin info %s key '%s' doesn't hold a string",
debugLocation.c_str(),
key.c_str());
return false;
}

const std::string& requestedVersion = checkIt->second.GetString();
if (versionValue == requestedVersion)
return true;
else {
TF_DEBUG(USDMAYA_PLUG_INFO_VERSION)
.Msg(
"Plugin info %s version check '%s' NOT match. "
"Requested '%s' but run under '%s'\n",
debugLocation.c_str(),
key.c_str(),
requestedVersion.c_str(),
versionValue.c_str());

return false;
}
};

if (!checkFn(_Tokens->CheckPythonKey, pythonVersion)) {
return false;
}

if (!checkFn(_Tokens->CheckUsdKey, usdVersion)) {
return false;
}

if (!checkFn(_Tokens->CheckMayaUsdKey, mayaUsdVersion)) {
return false;
}

return true;
}

/*! \brief Extract plugin path from MayaUsd plug info
\return Valid absolute path for properly configured json file. Empty string otherwise.
*/
std::string getPluginPath(
const JsObject& plugInfo,
const std::string& mayaUsdPluginInfoPath,
const std::string& debugLocation)
{
std::string pluginIncludePath;

JsObject::const_iterator pluginPathIt = plugInfo.find(_Tokens->PlugPathKey);
if (pluginPathIt != plugInfo.end()) {
if (!pluginPathIt->second.IsString()) {
TF_RUNTIME_ERROR(
"Plugin info %s key %s doesn't hold a string",
debugLocation.c_str(),
pluginPathIt->first.c_str());
} else {
const std::string& includePath = pluginPathIt->second.GetString();
if (TfIsRelativePath(includePath)) {
pluginIncludePath = TfStringCatPaths(mayaUsdPluginInfoPath, includePath);
} else {
pluginIncludePath = includePath;
}
}
}

return pluginIncludePath;
}

} // namespace

namespace MAYAUSD_NS_DEF {

void registerVersionedPlugins()
{
static std::once_flag once;
std::call_once(once, []() {
const std::string pythonVersion = TOSTRING(MAYA_PY_VERSION);
const std::string usdVersion = TOSTRING(MAYA_USD_VERSION);
const std::string mayaUsdVersion = TOSTRING(MAYAUSD_VERSION);

std::vector<std::string> pluginsToRegister;

const std::string paths = TfGetenv("MAYA_PXR_PLUGINPATH_NAME");
for (const auto& path : TfStringSplit(paths, ARCH_PATH_LIST_SEP)) {
if (path.empty()) {
continue;
}

if (TfIsRelativePath(path)) {
TF_CODING_ERROR(
"Relative paths are unsupported for MAYA_PXR_PLUGINPATH_NAME: '%s'",
path.c_str());
continue;
}

// Append the maya usd plug info file name
std::string plugInfoPath
= TfStringCatPaths(path, _Tokens->MayaUsdPlugInfoName.GetString());

JsObject plugInfoObject;
if (!readPlugInfoObject(plugInfoPath, plugInfoObject)) {
continue;
}

JsObject::const_iterator topIncludesIt = plugInfoObject.find(_Tokens->IncludesKey);
if (topIncludesIt == plugInfoObject.end() || !topIncludesIt->second.IsArray()) {
TF_RUNTIME_ERROR(
"Plugin info file %s key '%s' doesn't hold an object",
plugInfoPath.c_str(),
_Tokens->IncludesKey.GetString().c_str());
continue;
}

const JsArray& pluginIncludes = topIncludesIt->second.GetJsArray();
for (size_t i = 0, n = pluginIncludes.size(); i != n; ++i) {
const std::string debugLocation = TfStringPrintf(
"file %s %s[%zd]", plugInfoPath.c_str(), topIncludesIt->first.c_str(), i);

if (!pluginIncludes[i].IsObject()) {
TF_RUNTIME_ERROR(
"Plugin info %s key '%s' doesn't hold an object",
debugLocation.c_str(),
topIncludesIt->first.c_str());
continue;
}

const JsObject& topPluginObject = pluginIncludes[i].GetJsObject();
if (!checkPluginVersions(
topPluginObject,
pythonVersion,
usdVersion,
mayaUsdVersion,
debugLocation)) {
// skipping plugin because it didn't pass version check
continue;
}

std::string pluginPath = getPluginPath(topPluginObject, path, debugLocation);
if (!pluginPath.empty()) {
TF_DEBUG(USDMAYA_PLUG_INFO_VERSION)
.Msg(
"Plugin info %s. Will request registration for '%s'\n",
debugLocation.c_str(),
pluginPath.c_str());

pluginsToRegister.push_back(pluginPath);
}
}
}

PlugRegistry::GetInstance().RegisterPlugins(pluginsToRegister);
});
}
} // namespace MAYAUSD_NS_DEF
Loading

0 comments on commit 2700caf

Please sign in to comment.