├── .gitignore ├── plugInfo.json ├── external ├── CMakeLists.txt └── z85 │ ├── LICENSE │ ├── z85_impl.cpp │ ├── z85.hpp │ ├── z85.h │ └── z85.c ├── URIResolver ├── debug_codes.h ├── obfuscate_pass │ ├── main.cpp │ └── CMakeLists.txt ├── debug_codes.cpp ├── plugInfo.json.in ├── sql.h ├── memory_asset.cpp ├── memory_asset.h ├── resolver.h ├── README.md ├── CMakeLists.txt ├── resolver.cpp └── sql.cpp ├── stressTest ├── CMakeLists.txt └── main.cxx ├── CMakeLists.txt ├── LICENSE.md ├── README.md ├── .clang-format └── cmake ├── FindUSD.cmake ├── FindTBB.cmake └── FindMySQL.cmake /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .vscode 3 | cmake-* 4 | -------------------------------------------------------------------------------- /plugInfo.json: -------------------------------------------------------------------------------- 1 | { 2 | "Includes":["*/resources/"] 3 | } -------------------------------------------------------------------------------- /external/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | install( 2 | FILES z85/LICENSE 3 | DESTINATION licenses) 4 | -------------------------------------------------------------------------------- /URIResolver/debug_codes.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "pxr/base/tf/debug.h" 4 | #include "pxr/pxr.h" 5 | 6 | PXR_NAMESPACE_OPEN_SCOPE 7 | 8 | TF_DEBUG_CODES(USD_URI_RESOLVER, USD_URI_SQL_RESOLVER); 9 | 10 | PXR_NAMESPACE_CLOSE_SCOPE 11 | -------------------------------------------------------------------------------- /URIResolver/obfuscate_pass/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main(int argc, char* argv[]) { 5 | if (argc < 2) { return -1; } 6 | std::cout << z85::encode_with_padding(std::string(argv[1])); 7 | return 0; 8 | } 9 | -------------------------------------------------------------------------------- /URIResolver/obfuscate_pass/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(APP_NAME uri_resolver_obfuscate_pass) 2 | 3 | add_executable(${APP_NAME} ${Z85_SRC} main.cpp) 4 | target_include_directories(${APP_NAME} SYSTEM PRIVATE "${EXTERNAL_INCLUDE_DIR}") 5 | 6 | install( 7 | TARGETS ${APP_NAME} 8 | DESTINATION bin) 9 | -------------------------------------------------------------------------------- /URIResolver/debug_codes.cpp: -------------------------------------------------------------------------------- 1 | #include "debug_codes.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | PXR_NAMESPACE_OPEN_SCOPE 8 | 9 | TF_REGISTRY_FUNCTION(TfDebug) { 10 | TF_DEBUG_ENVIRONMENT_SYMBOL( 11 | USD_URI_RESOLVER, "Usd URI handling asset resolver."); 12 | 13 | TF_DEBUG_ENVIRONMENT_SYMBOL( 14 | USD_URI_SQL_RESOLVER, "Usd SQL asset resolver."); 15 | } 16 | 17 | PXR_NAMESPACE_CLOSE_SCOPE 18 | -------------------------------------------------------------------------------- /URIResolver/plugInfo.json.in: -------------------------------------------------------------------------------- 1 | { 2 | "Plugins": [ 3 | { 4 | "Info": { 5 | "Types": { 6 | "URIResolver": { 7 | "bases": [ 8 | "ArResolver" 9 | ] 10 | } 11 | } 12 | }, 13 | "LibraryPath": "../URIResolver.${DSO_EXT}", 14 | "Name": "URIResolver", 15 | "ResourcePath": "resources", 16 | "Root": "..", 17 | "Type": "library" 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /URIResolver/sql.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | #include 8 | 9 | #include 10 | #include 11 | 12 | PXR_NAMESPACE_OPEN_SCOPE 13 | 14 | struct SQLConnection; 15 | class SQLResolver { 16 | public: 17 | SQLResolver(); 18 | ~SQLResolver(); 19 | void clear(); 20 | 21 | std::string find_asset(const std::string& path); 22 | bool matches_schema(const std::string& path); 23 | double get_timestamp(const std::string& path); 24 | std::shared_ptr open_asset(const std::string& path); 25 | 26 | private: 27 | using connection_pair = std::pair; 28 | SQLConnection* get_connection(bool create); 29 | std::mutex connections_mutex; 30 | std::vector connections; 31 | }; 32 | 33 | PXR_NAMESPACE_CLOSE_SCOPE 34 | -------------------------------------------------------------------------------- /stressTest/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | find_package(Boost REQUIRED COMPONENTS python) 2 | find_package(PythonLibs 2.7 REQUIRED) 3 | find_package(TBB REQUIRED) 4 | find_package(MySQL REQUIRED) 5 | 6 | link_directories(${USD_LIBRARY_DIR}) 7 | 8 | set(SRC main.cxx) 9 | 10 | add_executable(stress ${SRC} ${Z85_SRC}) 11 | set_target_properties(stress PROPERTIES PREFIX "") 12 | set_target_properties(stress PROPERTIES INSTALL_RPATH_USE_LINK_PATH ON) 13 | target_link_libraries(stress PRIVATE 14 | ${Boost_LIBRARIES} 15 | ${PYTHON_LIBRARIES} 16 | ${MYSQL_LIBRARIES}) 17 | target_link_libraries(stress PRIVATE arch tf plug vt ar sdf usd usdGeom) 18 | target_include_directories(stress SYSTEM PRIVATE "${USD_INCLUDE_DIR}") 19 | target_include_directories(stress SYSTEM PRIVATE "${Boost_INCLUDE_DIRS}") 20 | target_include_directories(stress SYSTEM PRIVATE "${PYTHON_INCLUDE_DIRS}") 21 | target_include_directories(stress SYSTEM PRIVATE "${MYSQL_INCLUDE_DIR}") 22 | target_include_directories(stress SYSTEM PRIVATE "${TBB_INCLUDE_DIRS}") 23 | target_include_directories(stress SYSTEM PRIVATE "${EXTERNAL_INCLUDE_DIR}") 24 | 25 | install( 26 | TARGETS stress 27 | DESTINATION bin) 28 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(usd-uri-resolver) 2 | 3 | cmake_minimum_required(VERSION 3.12) 4 | 5 | if (UNIX AND NOT APPLE) 6 | set(LINUX TRUE) 7 | endif() 8 | 9 | list(INSERT CMAKE_MODULE_PATH 0 "${CMAKE_SOURCE_DIR}/cmake") 10 | 11 | find_package(USD REQUIRED) 12 | 13 | if (LINUX) 14 | set(DSO_EXT "so") 15 | elseif (APPLE) 16 | set(DSO_EXT "dylib") 17 | else () 18 | set(DSO_EXT "dll") 19 | endif () 20 | 21 | # TODO: Windows support. 22 | add_compile_options(-Wall -DBUILD_OPTLEVEL_OPT -DBUILD_COMPONENT_SRC_PREFIX="") 23 | option(ENABLE_RESOLVER_BUILD "Enabling building the uri resolver." On) 24 | option(ENABLE_STRESSTEST_BUILD "Enabling building stress test for the resolver." Off) 25 | 26 | set(EXTERNAL_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external) 27 | 28 | set(Z85_SRC 29 | ${CMAKE_CURRENT_SOURCE_DIR}/external/z85/z85_impl.cpp 30 | ${CMAKE_CURRENT_SOURCE_DIR}/external/z85/z85.c) 31 | 32 | if (ENABLE_RESOLVER_BUILD) 33 | add_subdirectory(URIResolver) 34 | endif () 35 | 36 | if (ENABLE_STRESSTEST_BUILD) 37 | add_subdirectory(stressTest) 38 | endif () 39 | 40 | add_subdirectory(external) 41 | 42 | install( 43 | FILES plugInfo.json 44 | DESTINATION .) 45 | -------------------------------------------------------------------------------- /URIResolver/memory_asset.cpp: -------------------------------------------------------------------------------- 1 | #include "memory_asset.h" 2 | 3 | #include 4 | 5 | #include 6 | 7 | PXR_NAMESPACE_OPEN_SCOPE 8 | 9 | MemoryAsset::MemoryAsset(const char* raw, size_t size) : data_size(size) { 10 | if (size > 0) { 11 | auto* d = static_cast(malloc(data_size)); 12 | memcpy(d, raw, data_size); 13 | data.reset(d, [](char* p) { free(p); }); 14 | } 15 | } 16 | 17 | size_t MemoryAsset::_GetSize() const { return data_size; } 18 | 19 | std::shared_ptr MemoryAsset::_GetBuffer() const { return data; } 20 | 21 | size_t MemoryAsset::_Read(void* buffer, size_t count, size_t offset) const { 22 | if (data == nullptr || offset >= data_size) { return 0; } 23 | const auto copied = std::min(data_size - offset, count); 24 | memcpy(buffer, data.get() + offset, copied); 25 | return copied; 26 | } 27 | 28 | std::pair MemoryAsset::_GetFileUnsafe() const { 29 | if (temp == nullptr) { 30 | std::lock_guard lock(temp_mutex); 31 | if (temp == nullptr) { 32 | temp = tmpfile(); 33 | if (!TF_VERIFY(temp != nullptr)) { 34 | return {nullptr, 0}; 35 | } 36 | fwrite(data.get(), data_size, 1, temp); 37 | } 38 | } 39 | return {temp, 0}; 40 | } 41 | 42 | PXR_NAMESPACE_CLOSE_SCOPE 43 | -------------------------------------------------------------------------------- /external/z85/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013, Stanislav Artemkin 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | Redistributions in binary form must reproduce the above copyright notice, this 11 | list of conditions and the following disclaimer in the documentation and/or 12 | other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 21 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017-2018 Luma Pictures . All rights reserved. 2 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 3 | 4 | 5 | Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 6 | Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 7 | All advertising materials mentioning features or use of this software must display the following acknowledgement: 8 | This product includes software developed by the the organization . 9 | Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 10 | 11 | 12 | THIS SOFTWARE IS PROVIDED BY COPYRIGHT HOLDER "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /URIResolver/memory_asset.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | #include 8 | 9 | PXR_NAMESPACE_OPEN_SCOPE 10 | 11 | class MemoryAsset final : public ArAsset { 12 | public: 13 | MemoryAsset(const char* raw, size_t size); 14 | ~MemoryAsset() override = default; 15 | 16 | MemoryAsset(const MemoryAsset&) = delete; 17 | MemoryAsset(MemoryAsset&&) = delete; 18 | MemoryAsset& operator=(const MemoryAsset&) = delete; 19 | MemoryAsset& operator=(MemoryAsset&&) = delete; 20 | 21 | #if AR_VERSION == 2 22 | size_t GetSize() const override { return _GetSize(); } 23 | std::shared_ptr GetBuffer() const override { 24 | return _GetBuffer(); 25 | }; 26 | size_t Read(void* buffer, size_t count, size_t offset) const override { 27 | return _Read(buffer, count, offset); 28 | }; 29 | std::pair GetFileUnsafe() const override { 30 | return _GetFileUnsafe(); 31 | }; 32 | #else 33 | size_t GetSize() override { return _GetSize(); } 34 | std::shared_ptr GetBuffer() override { 35 | return _GetBuffer(); 36 | }; 37 | size_t Read(void* buffer, size_t count, size_t offset) override { 38 | return _Read(buffer, count, offset); 39 | }; 40 | std::pair GetFileUnsafe() override { 41 | return _GetFileUnsafe(); 42 | }; 43 | #endif 44 | 45 | private: 46 | size_t _GetSize() const; 47 | std::shared_ptr _GetBuffer() const; 48 | size_t _Read(void* buffer, size_t count, size_t offset) const; 49 | std::pair _GetFileUnsafe() const; 50 | 51 | // We are using a shared ptr to store the data instead of a std::vector, 52 | // because the ArAsset interface requires return of the same struct, which 53 | // need to outlive the asset. 54 | std::shared_ptr data; 55 | size_t data_size; 56 | mutable std::mutex temp_mutex; 57 | // This is going to be from tmpfile(), so no need to manually free it. 58 | mutable FILE* temp = nullptr; 59 | }; 60 | 61 | PXR_NAMESPACE_CLOSE_SCOPE 62 | -------------------------------------------------------------------------------- /URIResolver/resolver.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | PXR_NAMESPACE_OPEN_SCOPE 12 | 13 | /// \class URIResolver 14 | /// 15 | /// Generic resolver to handle Luma needs. 16 | /// 17 | class URIResolver : public ArDefaultResolver { 18 | public: 19 | URIResolver(); 20 | ~URIResolver() override; 21 | 22 | #if AR_VERSION == 2 23 | ArResolvedPath _Resolve(const std::string& assetPath) const override; 24 | 25 | ArResolvedPath _ResolveForNewAsset(const std::string& assetPath) const override; 26 | 27 | std::string _CreateIdentifierForNewAsset( 28 | const std::string& assetPath, 29 | const ArResolvedPath& anchorAssetPath) const override; 30 | 31 | ArTimestamp _GetModificationTimestamp( 32 | const std::string& assetPath, 33 | const ArResolvedPath& resolvedPath) const override; 34 | 35 | std::shared_ptr _OpenAsset( 36 | const ArResolvedPath& resolvedPath) const override; 37 | #else 38 | std::string Resolve(const std::string& path) override; 39 | 40 | bool IsRelativePath(const std::string& path) override; 41 | 42 | std::string ResolveWithAssetInfo( 43 | const std::string& path, ArAssetInfo* assetInfo) override; 44 | 45 | // Quick workaround for a bug in USD 0.8.4. 46 | std::string AnchorRelativePath( 47 | const std::string& anchorPath, const std::string& path) override; 48 | 49 | VtValue GetModificationTimestamp( 50 | const std::string& path, const std::string& resolvedPath) override; 51 | 52 | bool FetchToLocalResolvedPath( 53 | const std::string& path, const std::string& resolvedPath) override; 54 | 55 | std::shared_ptr OpenAsset( 56 | const std::string& resolvedPath) override; 57 | #endif 58 | private: 59 | bool _ResolveSql( 60 | const std::string& assetPath, std::string& resolvedPath) const; 61 | 62 | bool _GetTimestampSql( 63 | const std::string& assetPath, double& timestamp) const; 64 | 65 | bool _OpenSqlAsset( 66 | const std::string& resolvedPath, std::shared_ptr& asset) const; 67 | }; 68 | 69 | PXR_NAMESPACE_CLOSE_SCOPE 70 | -------------------------------------------------------------------------------- /URIResolver/README.md: -------------------------------------------------------------------------------- 1 | # Usage of the URI Resolver 2 | 3 | Currently the URI Resolver provides support for an alternative protocol, when referencing other usd files, namely loading data from an SQL database. 4 | 5 | ## SQL Protocol 6 | 7 | #### Request URL 8 | 9 | SQL protocol can be accessed, by using sql:// when referencing assets in a USD file. 10 | 11 | Server name 12 | - Can be either an IP address or a host name. 13 | 14 | Asset path 15 | - Need to start with /, just like on a normal file SYSTEM 16 | - Need to end with an extension, that represent the data storage format in the database (usd currently) 17 | 18 | #### Table layout 19 | 20 | The SQL resolver expects a table, with 3 entries. 21 | - path - CHAR / VARCHAR containing the path to the asset 22 | - data - (LONG/MEDIUM/SHORT)BLOB containing the data. 23 | - timestamp - TIMESTAMP containing the last asset modification time. Set the expression to ON UPDATE CURRENT_TIMESTAMP to always keep up to date with changes, and make sure timezones are setup correctly on the databases. 24 | 25 | #### Environment variables supported by the resolver 26 | 27 | Each environment variable can be either setup globally, or server specific. First the server specific variable is queried, then the global one, then the default value is used. Server specific variables can be setup by prefixing the environment variable with _ . For example USD_SQL_PASSWD becomes sv-dev01.luma.mel_USD_SQL_PASSWD if specialized for that given server. 28 | 29 | - USD_SQL_DB - Database name on the SQL server. Default value is usd. 30 | - USD_SQL_USER - User to access the database. Default value is root. 31 | - USD_SQL_PASSWD - Password for the user to access the database. Default value is the obfuscated version of 12345678. 32 | - USD_SQL_PORT - Port to access the database. Default value is 3306. 33 | - USD_SQL_TABLE - Name of the table containing the data. Default value is headers. 34 | - USD_SQL_CACHE_PATH - Name of the local cache path to save usd files. Default value is /tmp. 35 | 36 | #### Password obfuscation 37 | 38 | To avoid storing passwords directly in pipeline files (typically python), the resolver provides a small application that obfuscates passwords. The usage is simple, just call uri_resolver_obfuscate_pass and use the returned value when setting up environment variables. The goal of this is not to provide absolute safety, but to hide passwords from the non-coder eyes. 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # USD-URI-resolver 2 | A generic, URI based resolver for USD, support custom plugins. 3 | 4 | ## Project Goals 5 | * Resolve custom URI based for USD files. 6 | * Provide a generic plugin registry to extend the resolver without modifying its source code. 7 | * Include example resolvers supporting database based files. 8 | 9 | ## Features 10 | 11 | ### Current 12 | * URIResolver - custom resolver for USD. 13 | * usd_sql::SQL - MySQL database access. 14 | * obfuscate_pass - A simple tool to convert passwords to a z85 encoded string. WARNING !!! This is not for encrypting your password, but to hide it from artists in an environment. It is extremely simple to "decrypt" and offers no protection. 15 | 16 | ### Planned 17 | * Create a plugin registry for extending URIResolver. 18 | * Move the SQL accessor to a spearete plugin and load it dynamically. 19 | * Windows support. 20 | * Simplify the build process. 21 | 22 | ## Building 23 | 24 | You'll need the following libraries to build the project; newer versions most likely work as well, but they are not tested. Currently, the cmake files are looking for the openexr libs, but only use the half headers. This will be removed in the future. 25 | 26 | | Package | Version | 27 | | ----------------- | -------------- | 28 | | USD | 0.7.6+ (stock) | 29 | | TBB | 4.3+ | 30 | | OpenEXR | 2.2.0+ | 31 | | Boost | 1.61.0+ | 32 | | MySQL Connector C | 6.1.9+ | 33 | | CMAKE | 2.8+ | 34 | 35 | You can download the MySql library [here](https://dev.mysql.com/downloads/connector/c/). 36 | 37 | There are two main ways to configure a library location. 1, configure an environment variable. 2, pass the location of the library using -D\=\ to cmake. This will be simplified soon, once we add proper find modules. 38 | 39 | * Point the USD\_ROOT environment variable to the location of the installed USD. 40 | * Point the MYSQL\_CONNECTOR\_ROOT variable to the location of the extracted MySql connector C library. 41 | * Pass OPENEXR\_LOCATION to the CMake command or setup the OPENEXR\_LOCATION environment variable. They have to point at a standard build of OpenEXR, including IlmBase. 42 | * Point TBB\_ROOT\_DIR}, TBB\_INSTALL\_DIR or TBBROOT at your local TBB installation. 43 | 44 | ## Contributing 45 | TODO. 46 | 47 | ## Using the SQL resolver. 48 | Consult the README.md installed alongside the URIResolver. -------------------------------------------------------------------------------- /URIResolver/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(obfuscate_pass) 2 | 3 | set(PLUGIN_NAME URIResolver) 4 | 5 | if (USE_PYTHON_3) 6 | find_package(Python 3.0 COMPONENTS Development REQUIRED) 7 | else() 8 | find_package(Python 2.7 COMPONENTS Development REQUIRED) 9 | endif() 10 | 11 | find_package(Boost COMPONENTS python) 12 | if (NOT Boost_FOUND) 13 | set(python_version_nodot "${Python_VERSION_MAJOR}${Python_VERSION_MINOR}") 14 | find_package(Boost COMPONENTS python${python_version_nodot} REQUIRED) 15 | endif() 16 | find_package(TBB REQUIRED) 17 | find_package(MySQL REQUIRED) 18 | 19 | link_directories(${USD_LIBRARY_DIR}) 20 | 21 | set(SRC 22 | debug_codes.cpp 23 | memory_asset.cpp 24 | resolver.cpp 25 | sql.cpp) 26 | 27 | add_library(${PLUGIN_NAME} SHARED ${Z85_SRC} ${SRC}) 28 | set_target_properties(${PLUGIN_NAME} PROPERTIES PREFIX "") 29 | set_target_properties(${PLUGIN_NAME} PROPERTIES INSTALL_RPATH_USE_LINK_PATH ON) 30 | target_link_libraries(${PLUGIN_NAME} PRIVATE ${Boost_LIBRARIES} ${Python_LIBRARIES}) 31 | target_link_libraries(${PLUGIN_NAME} PRIVATE ${TBB_LIBRARIES}) 32 | target_link_libraries(${PLUGIN_NAME} PRIVATE arch tf plug vt ar ${MYSQL_LIB}) 33 | target_include_directories(${PLUGIN_NAME} SYSTEM PRIVATE "${USD_INCLUDE_DIR}") 34 | target_include_directories(${PLUGIN_NAME} SYSTEM PRIVATE "${Boost_INCLUDE_DIRS}") 35 | target_include_directories(${PLUGIN_NAME} SYSTEM PRIVATE "${Python_INCLUDE_DIRS}") 36 | target_include_directories(${PLUGIN_NAME} SYSTEM PRIVATE "${MYSQL_INCLUDE_DIR}") 37 | target_include_directories(${PLUGIN_NAME} SYSTEM PRIVATE "${TBB_INCLUDE_DIRS}") 38 | target_include_directories(${PLUGIN_NAME} SYSTEM PRIVATE "${EXTERNAL_INCLUDE_DIR}") 39 | 40 | if (MSVC) 41 | # Make sure WinDef.h doesn't define min and max macros which 42 | # will conflict with std::min() and std::max() 43 | target_compile_definitions(${PLUGIN_NAME} PRIVATE NOMINMAX) 44 | 45 | # The /Zc:inline option strips out the "arch_ctor_" symbols used for 46 | # library initialization by ARCH_CONSTRUCTOR starting in Visual Studio 2019, 47 | # causing release builds to fail. Disable the option for this and later 48 | # versions. 49 | # 50 | # For more details, see: 51 | # https://developercommunity.visualstudio.com/content/problem/914943/zcinline-removes-extern-symbols-inside-anonymous-n.html 52 | if (MSVC_VERSION GREATER_EQUAL 1920) 53 | target_compile_options(${PLUGIN_NAME} PRIVATE "/Zc:inline-") 54 | else() 55 | target_compile_options(${PLUGIN_NAME} PRIVATE "/Zc:inline") 56 | endif() 57 | endif() 58 | 59 | configure_file( 60 | "${CMAKE_CURRENT_SOURCE_DIR}/plugInfo.json.in" 61 | "${CMAKE_CURRENT_BINARY_DIR}/plugInfo.json") 62 | 63 | install( 64 | TARGETS ${PLUGIN_NAME} 65 | DESTINATION .) 66 | 67 | install( 68 | FILES "${CMAKE_CURRENT_BINARY_DIR}/plugInfo.json" 69 | DESTINATION ${PLUGIN_NAME}/resources/) 70 | 71 | install( 72 | FILES README.md 73 | DESTINATION docs) 74 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | # BasedOnStyle: Google 4 | AccessModifierOffset: -4 5 | AlignAfterOpenBracket: AlwaysBreak 6 | AlignConsecutiveAssignments: false 7 | AlignConsecutiveDeclarations: false 8 | AlignEscapedNewlines: Left 9 | AlignOperands: true 10 | AlignTrailingComments: true 11 | AllowAllParametersOfDeclarationOnNextLine: true 12 | AllowShortBlocksOnASingleLine: true 13 | AllowShortCaseLabelsOnASingleLine: false 14 | AllowShortFunctionsOnASingleLine: All 15 | AllowShortIfStatementsOnASingleLine: true 16 | AllowShortLoopsOnASingleLine: true 17 | AlwaysBreakAfterDefinitionReturnType: None 18 | AlwaysBreakAfterReturnType: None 19 | AlwaysBreakBeforeMultilineStrings: true 20 | AlwaysBreakTemplateDeclarations: true 21 | BinPackArguments: true 22 | BinPackParameters: true 23 | BraceWrapping: 24 | AfterClass: false 25 | AfterControlStatement: false 26 | AfterEnum: false 27 | AfterFunction: false 28 | AfterNamespace: false 29 | AfterObjCDeclaration: false 30 | AfterStruct: false 31 | AfterUnion: false 32 | AfterExternBlock: false 33 | BeforeCatch: false 34 | BeforeElse: false 35 | IndentBraces: false 36 | SplitEmptyFunction: true 37 | SplitEmptyRecord: true 38 | SplitEmptyNamespace: true 39 | BreakBeforeBinaryOperators: None 40 | BreakBeforeBraces: Attach 41 | BreakBeforeInheritanceComma: false 42 | BreakBeforeTernaryOperators: true 43 | BreakConstructorInitializersBeforeComma: false 44 | BreakConstructorInitializers: BeforeColon 45 | BreakAfterJavaFieldAnnotations: false 46 | BreakStringLiterals: true 47 | ColumnLimit: 80 48 | CommentPragmas: '^ IWYU pragma:' 49 | CompactNamespaces: false 50 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 51 | ConstructorInitializerIndentWidth: 4 52 | ContinuationIndentWidth: 4 53 | Cpp11BracedListStyle: true 54 | DerivePointerAlignment: true 55 | DisableFormat: false 56 | ExperimentalAutoDetectBinPacking: false 57 | FixNamespaceComments: true 58 | IncludeBlocks: Preserve 59 | IncludeCategories: 60 | - Regex: '^' 61 | Priority: 2 62 | - Regex: '^<.*\.h>' 63 | Priority: 1 64 | - Regex: '^<.*' 65 | Priority: 2 66 | - Regex: '.*' 67 | Priority: 3 68 | IncludeIsMainRegex: '([-_](test|unittest))?$' 69 | IndentCaseLabels: true 70 | IndentPPDirectives: None 71 | IndentWidth: 4 72 | IndentWrappedFunctionNames: false 73 | JavaScriptQuotes: Leave 74 | JavaScriptWrapImports: true 75 | KeepEmptyLinesAtTheStartOfBlocks: false 76 | MacroBlockBegin: '' 77 | MacroBlockEnd: '' 78 | MaxEmptyLinesToKeep: 1 79 | NamespaceIndentation: None 80 | ObjCBlockIndentWidth: 2 81 | ObjCSpaceAfterProperty: false 82 | ObjCSpaceBeforeProtocolList: false 83 | PenaltyBreakAssignment: 2 84 | PenaltyBreakBeforeFirstCallParameter: 1 85 | PenaltyBreakComment: 300 86 | PenaltyBreakFirstLessLess: 120 87 | PenaltyBreakString: 1000 88 | PenaltyExcessCharacter: 1000000 89 | PenaltyReturnTypeOnItsOwnLine: 200 90 | PointerAlignment: Left 91 | RawStringFormats: 92 | - Language: TextProto 93 | Delimiters: 94 | - 'pb' 95 | - 'proto' 96 | EnclosingFunctions: 97 | - 'PARSE_TEXT_PROTO' 98 | BasedOnStyle: google 99 | - Language: Cpp 100 | Delimiters: 101 | - 'cc' 102 | - 'cpp' 103 | BasedOnStyle: llvm 104 | CanonicalDelimiter: 'cc' 105 | ReflowComments: true 106 | SortIncludes: true 107 | SortUsingDeclarations: true 108 | SpaceAfterCStyleCast: false 109 | SpaceAfterTemplateKeyword: true 110 | SpaceBeforeAssignmentOperators: true 111 | SpaceBeforeParens: ControlStatements 112 | SpaceInEmptyParentheses: false 113 | SpacesBeforeTrailingComments: 1 114 | SpacesInAngles: false 115 | SpacesInContainerLiterals: true 116 | SpacesInCStyleCastParentheses: false 117 | SpacesInParentheses: false 118 | SpacesInSquareBrackets: false 119 | Standard: Auto 120 | TabWidth: 4 121 | UseTab: Never 122 | ... 123 | 124 | -------------------------------------------------------------------------------- /external/z85/z85_impl.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Stanislav Artemkin . 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are 6 | * met: 7 | * 8 | * * Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above 11 | * copyright notice, this list of conditions and the following disclaimer 12 | * in the documentation and/or other materials provided with the 13 | * distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 18 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 19 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 21 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | * 27 | * Implementation of 32/Z85 specification (http://rfc.zeromq.org/spec:32/Z85) 28 | * Source repository: http://github.com/artemkin/z85 29 | */ 30 | 31 | #include "z85.hpp" 32 | 33 | #include 34 | 35 | #include "z85.h" 36 | 37 | 38 | namespace z85 39 | { 40 | 41 | std::string encode_with_padding(const char* source, size_t inputSize) 42 | { 43 | if (!source || inputSize == 0) 44 | { 45 | return std::string(); 46 | } 47 | 48 | std::string buf; 49 | buf.resize(Z85_encode_with_padding_bound(inputSize)); 50 | 51 | const size_t encodedBytes = Z85_encode_with_padding(source, &buf[0], inputSize); 52 | assert(encodedBytes == buf.size()); (void)encodedBytes; 53 | 54 | return buf; 55 | } 56 | 57 | std::string encode_with_padding(const std::string& source) 58 | { 59 | return encode_with_padding(source.c_str(), source.size()); 60 | } 61 | 62 | std::string decode_with_padding(const char* source, size_t inputSize) 63 | { 64 | if (!source || inputSize == 0) 65 | { 66 | return std::string(); 67 | } 68 | 69 | const size_t bufSize = Z85_decode_with_padding_bound(source, inputSize); 70 | if (bufSize == 0) 71 | { 72 | assert(!"wrong padding"); 73 | return std::string(); 74 | } 75 | 76 | std::string buf; 77 | buf.resize(bufSize); 78 | 79 | const size_t decodedBytes = Z85_decode_with_padding(source, &buf[0], inputSize); 80 | assert(decodedBytes == buf.size()); (void)decodedBytes; 81 | 82 | return buf; 83 | } 84 | 85 | std::string decode_with_padding(const std::string& source) 86 | { 87 | return decode_with_padding(source.c_str(), source.size()); 88 | } 89 | 90 | std::string encode(const char* source, size_t inputSize) 91 | { 92 | if (!source || inputSize == 0) 93 | { 94 | return std::string(); 95 | } 96 | 97 | std::string buf; 98 | buf.resize(Z85_encode_bound(inputSize)); 99 | 100 | const size_t encodedBytes = Z85_encode(source, &buf[0], inputSize); 101 | if (encodedBytes == 0) 102 | { 103 | assert(!"wrong input size"); 104 | return std::string(); 105 | } 106 | 107 | return buf; 108 | } 109 | 110 | std::string encode(const std::string& source) 111 | { 112 | return encode(source.c_str(), source.size()); 113 | } 114 | 115 | std::string decode(const char* source, size_t inputSize) 116 | { 117 | if (!source || inputSize == 0) 118 | { 119 | return std::string(); 120 | } 121 | 122 | std::string buf; 123 | buf.resize(Z85_decode_bound(inputSize)); 124 | 125 | const size_t decodedBytes = Z85_decode(source, &buf[0], inputSize); 126 | if (decodedBytes == 0) 127 | { 128 | assert(!"wrong input size"); 129 | return std::string(); 130 | } 131 | 132 | return buf; 133 | } 134 | 135 | std::string decode(const std::string& source) 136 | { 137 | return decode(source.c_str(), source.size()); 138 | } 139 | 140 | } // namespace z85 141 | 142 | -------------------------------------------------------------------------------- /external/z85/z85.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Stanislav Artemkin . 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are 6 | * met: 7 | * 8 | * * Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above 11 | * copyright notice, this list of conditions and the following disclaimer 12 | * in the documentation and/or other materials provided with the 13 | * distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 18 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 19 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 21 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | * 27 | * Implementation of 32/Z85 specification (http://rfc.zeromq.org/spec:32/Z85) 28 | * Source repository: http://github.com/artemkin/z85 29 | */ 30 | 31 | #pragma once 32 | 33 | #include 34 | #include 35 | 36 | // Used to forbid implicit std::string construction from const char* 37 | #if __cplusplus > 199711L // if C++11 38 | #define Z85_DELETE_FUNCTION_DEFINITION = delete 39 | #else 40 | #define Z85_DELETE_FUNCTION_DEFINITION 41 | #endif 42 | 43 | namespace z85 44 | { 45 | 46 | /******************************************************************************* 47 | * ZeroMQ Base-85 encoding/decoding functions with custom padding * 48 | *******************************************************************************/ 49 | 50 | /** 51 | * @brief Encodes 'inputSize' bytes from 'source'. 52 | * If 'inputSize' is not divisible by 4 with no remainder, 'source' is padded. 53 | * 54 | * @param source in, input buffer (binary string to be encoded) 55 | * @param inputSize in, number of bytes to be encoded 56 | * @return printable string 57 | */ 58 | std::string encode_with_padding(const char* source, size_t inputSize); 59 | std::string encode_with_padding(const std::string& source); 60 | 61 | std::string encode_with_padding(const char*) Z85_DELETE_FUNCTION_DEFINITION; 62 | 63 | /** 64 | * @brief Decodes 'inputSize' printable symbols from 'source', 65 | * encoded with encode_with_padding(). 66 | * 67 | * @param source in, input buffer (printable string to be decoded) 68 | * @param inputSize in, number of symbols to be decoded 69 | * @return decoded string 70 | */ 71 | std::string decode_with_padding(const char* source, size_t inputSize); 72 | std::string decode_with_padding(const std::string& source); 73 | 74 | std::string decode_with_padding(const char*) Z85_DELETE_FUNCTION_DEFINITION; 75 | 76 | 77 | /******************************************************************************* 78 | * ZeroMQ Base-85 encoding/decoding functions (specification compliant) * 79 | *******************************************************************************/ 80 | 81 | /** 82 | * @brief Encodes 'inputSize' bytes from 'source'. 83 | * If 'inputSize' is not divisible by 4 with no remainder, empty string is retured. 84 | * 85 | * @param source in, input buffer (binary string to be encoded) 86 | * @param inputSize in, number of bytes to be encoded 87 | * @return printable string 88 | */ 89 | std::string encode(const char* source, size_t inputSize); 90 | std::string encode(const std::string& source); 91 | 92 | std::string encode(const char*) Z85_DELETE_FUNCTION_DEFINITION; 93 | 94 | /** 95 | * @brief Decodes 'inputSize' printable symbols from 'source'. 96 | * If 'inputSize' is not divisible by 5 with no remainder, empty string is returned. 97 | * 98 | * @param source in, input buffer (printable string to be decoded) 99 | * @param inputSize in, number of symbols to be decoded 100 | * @return decoded string 101 | */ 102 | std::string decode(const char* source, size_t inputSize); 103 | std::string decode(const std::string& source); 104 | 105 | std::string decode(const char*) Z85_DELETE_FUNCTION_DEFINITION; 106 | 107 | } // namespace z85 108 | 109 | #undef Z85_DELETE_FUNCTION_DEFINITION 110 | 111 | -------------------------------------------------------------------------------- /stressTest/main.cxx: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | PXR_NAMESPACE_USING_DIRECTIVE 23 | 24 | TF_DEFINE_PRIVATE_TOKENS(_tokens, 25 | (counter)); 26 | 27 | namespace { 28 | 29 | constexpr auto HOST_ENV_VAR = "USD_SQL_DBHOST"; 30 | constexpr auto PORT_ENV_VAR = "USD_SQL_PORT"; 31 | constexpr auto DB_ENV_VAR = "USD_SQL_DB"; 32 | constexpr auto TABLE_ENV_VAR = "USD_SQL_TABLE"; 33 | constexpr auto USER_ENV_VAR = "USD_SQL_USER"; 34 | constexpr auto PASSWORD_ENV_VAR = "USD_SQL_PASSWD"; 35 | 36 | std::string get_env_var( 37 | const std::string& server_name, const std::string& env_var, 38 | const std::string& default_value) { 39 | const auto env_first = getenv((server_name + "_" + env_var).c_str()); 40 | if (env_first != nullptr) { return env_first; } 41 | const auto env_second = getenv(env_var.c_str()); 42 | if (env_second != nullptr) { return env_second; } 43 | return default_value; 44 | } 45 | 46 | std::string generate_stage(int i) { 47 | auto stage = UsdStage::CreateInMemory(TfStringPrintf("%i.usda", i)); 48 | 49 | auto scope = UsdGeomScope::Define(stage, SdfPath("/scope")); 50 | UsdGeomPrimvarsAPI primvarsAPI(scope); 51 | primvarsAPI.CreatePrimvar(_tokens->counter, SdfValueTypeNames->Int).Set(i); 52 | std::string ret; 53 | stage->GetRootLayer()->ExportToString(&ret); 54 | return ret; 55 | } 56 | 57 | int get_counter(UsdStageRefPtr stage) { 58 | auto scope = UsdGeomScope::Get(stage, SdfPath("/scope")); 59 | UsdGeomPrimvarsAPI primvarsAPI(scope); 60 | int ret = -1; 61 | primvarsAPI.GetPrimvar(_tokens->counter).Get(&ret); 62 | return ret; 63 | } 64 | 65 | bool insert_to_database(MYSQL* connection, const std::string& table_name, const std::string& path, const std::string& data) { 66 | std::string tmp; 67 | tmp.reserve(1024); 68 | std::stringstream query_delete; 69 | query_delete << "DELETE FROM `" << table_name << "` WHERE `path`=\"" << path << "\";"; 70 | const auto query_delete_str = query_delete.str(); 71 | auto query_ret = mysql_real_query(connection, query_delete_str.c_str(), query_delete_str.size()); 72 | if (query_ret != 0) { 73 | std::cerr << "Error executing delete query\n"; 74 | } 75 | std::stringstream query(tmp); 76 | query << "INSERT INTO `" << table_name 77 | << "` (`path`, `data`) VALUES (\"" << path 78 | << "\", _binary 0x"; 79 | query << std::hex << std::setfill('0') << std::uppercase; 80 | for (auto c : data) { 81 | query << std::setw(2) << static_cast(c); 82 | } 83 | query << ");"; 84 | 85 | const auto query_str = query.str(); 86 | query_ret = mysql_real_query(connection, query_str.c_str(), query_str.size()); 87 | return query_ret == 0; 88 | } 89 | 90 | } 91 | 92 | int main() { 93 | my_init(); 94 | 95 | const std::string server_name = getenv(HOST_ENV_VAR); 96 | const auto table_name = get_env_var(server_name, TABLE_ENV_VAR, "usd"); 97 | const auto server_user = get_env_var(server_name, USER_ENV_VAR, "root"); 98 | const auto compacted_default_pass = 99 | z85::encode_with_padding(std::string("12345678")); 100 | auto server_password = 101 | get_env_var(server_name, PASSWORD_ENV_VAR, compacted_default_pass); 102 | server_password = z85::decode_with_padding(server_password); 103 | const auto server_db = get_env_var(server_name, DB_ENV_VAR, "test"); 104 | const auto server_port = static_cast( 105 | atoi(get_env_var(server_name, PORT_ENV_VAR, "3306").c_str())); 106 | 107 | auto get_connection = [&] () -> MYSQL* { 108 | MYSQL* ret = mysql_init(nullptr); 109 | my_bool reconnect = 1; 110 | mysql_options(ret, MYSQL_OPT_RECONNECT, &reconnect); 111 | const auto* status = mysql_real_connect( 112 | ret, server_name.c_str(), server_user.c_str(), 113 | server_password.c_str(), server_db.c_str(), server_port, nullptr, 0); 114 | if (status == nullptr) { 115 | mysql_close(ret); 116 | return nullptr; 117 | } 118 | return ret; 119 | }; 120 | 121 | constexpr size_t num_threads = 6; 122 | constexpr size_t num_tests = 2048; 123 | 124 | std::vector connections; 125 | connections.reserve(num_threads); 126 | for (size_t i = 0; i < num_threads; ++i) { 127 | auto* conn = get_connection(); 128 | if (conn != nullptr) { 129 | connections.push_back(conn); 130 | } 131 | } 132 | 133 | std::atomic counter; 134 | counter.store(0); 135 | 136 | std::vector threads; 137 | threads.reserve(num_threads); 138 | auto thread_fun = [&] (MYSQL* conn) { 139 | 140 | const auto id = counter.fetch_add(1); 141 | const auto path = TfStringPrintf("/path%i.usda", id); 142 | auto c = counter.fetch_add(1); 143 | if (!insert_to_database(conn, table_name, path, generate_stage(c))) { 144 | std::cerr << "Error inserting into the database!\n"; 145 | return; 146 | } 147 | const auto sqlPath = TfStringPrintf("sql:///path%i.usda", id); 148 | auto stage = UsdStage::Open(sqlPath); 149 | for (size_t t = 0; t < num_tests; t += 1) { 150 | c = counter.fetch_add(1); 151 | insert_to_database(conn, table_name, path, generate_stage(c)); 152 | stage->Reload(); 153 | const auto c_new = get_counter(stage); 154 | if (c != c_new) { 155 | std::cerr << path << " incorrect counter " << c << " vs " << c_new << "\n"; 156 | } 157 | } 158 | }; 159 | for (auto* conn : connections) { 160 | threads.emplace_back(thread_fun, conn); 161 | } 162 | 163 | for (auto& thread : threads) { 164 | thread.join(); 165 | } 166 | 167 | for (auto* conn : connections) { 168 | mysql_close(conn); 169 | } 170 | 171 | return 0; 172 | } 173 | -------------------------------------------------------------------------------- /URIResolver/resolver.cpp: -------------------------------------------------------------------------------- 1 | #include "resolver.h" 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "debug_codes.h" 19 | #include "sql.h" 20 | 21 | /* 22 | * Depending on the asset count and access frequency, it could be better to 23 | * store the resolver paths in a sorted vector, rather than a map. That's way 24 | * faster when we are doing significantly more queries inserts. 25 | */ 26 | 27 | PXR_NAMESPACE_OPEN_SCOPE 28 | 29 | namespace { 30 | SQLResolver SQL; 31 | } 32 | 33 | AR_DEFINE_RESOLVER(URIResolver, ArResolver) 34 | 35 | URIResolver::URIResolver() : ArDefaultResolver() {} 36 | 37 | URIResolver::~URIResolver() { /*g_sql.clear();*/ 38 | } 39 | 40 | bool URIResolver::_ResolveSql( 41 | const std::string& assetPath, std::string& resolvedPath) const { 42 | if (SQL.matches_schema(assetPath)) { 43 | resolvedPath = SQL.find_asset(assetPath); 44 | return true; 45 | } 46 | return false; 47 | } 48 | 49 | bool URIResolver::_GetTimestampSql( 50 | const std::string& assetPath, double& timestamp) const { 51 | if (SQL.matches_schema(assetPath)) { 52 | timestamp = SQL.get_timestamp(assetPath); 53 | return true; 54 | } 55 | return false; 56 | } 57 | 58 | bool URIResolver::_OpenSqlAsset( 59 | const std::string& resolvedPath, std::shared_ptr& asset) const { 60 | if (SQL.matches_schema(resolvedPath)) { 61 | asset = SQL.open_asset(resolvedPath); 62 | return true; 63 | } 64 | return false; 65 | } 66 | 67 | #if AR_VERSION == 2 68 | ArResolvedPath URIResolver::_Resolve(const std::string& assetPath) const { 69 | TF_DEBUG(USD_URI_RESOLVER).Msg("_Resolve('%s')\n", assetPath.c_str()); 70 | std::string resolvedPath; 71 | if (_ResolveSql(assetPath, resolvedPath)) { 72 | return ArResolvedPath(resolvedPath); 73 | } 74 | return ArDefaultResolver::_Resolve(assetPath); 75 | } 76 | 77 | ArResolvedPath URIResolver::_ResolveForNewAsset( 78 | const std::string& assetPath) const { 79 | TF_DEBUG(USD_URI_RESOLVER) 80 | .Msg("_ResolveForNewAsset('%s')\n", assetPath.c_str()); 81 | std::string resolvedPath; 82 | if (_ResolveSql(assetPath, resolvedPath)) { 83 | return ArResolvedPath(resolvedPath); 84 | } 85 | return ArDefaultResolver::_ResolveForNewAsset(assetPath); 86 | } 87 | 88 | std::string URIResolver::_CreateIdentifierForNewAsset( 89 | const std::string& assetPath, 90 | const ArResolvedPath& anchorAssetPath) const { 91 | TF_DEBUG(USD_URI_RESOLVER) 92 | .Msg( 93 | "_CreateIdentifierForNewAsset('%s', '%s')\n", assetPath.c_str(), 94 | anchorAssetPath.GetPathString().c_str()); 95 | return SQL.matches_schema(assetPath) 96 | ? assetPath 97 | : ArDefaultResolver::_CreateIdentifierForNewAsset( 98 | assetPath, anchorAssetPath); 99 | } 100 | 101 | ArTimestamp URIResolver::_GetModificationTimestamp( 102 | const std::string& assetPath, const ArResolvedPath& resolvedPath) const { 103 | TF_DEBUG(USD_URI_RESOLVER) 104 | .Msg( 105 | "GetModificationTimestamp('%s', '%s')\n", assetPath.c_str(), 106 | resolvedPath.GetPathString().c_str()); 107 | double timestamp; 108 | if (_GetTimestampSql(assetPath, timestamp)) { 109 | return ArTimestamp(timestamp); 110 | } 111 | return ArDefaultResolver::_GetModificationTimestamp( 112 | assetPath, resolvedPath); 113 | } 114 | 115 | std::shared_ptr URIResolver::_OpenAsset( 116 | const ArResolvedPath& resolvedPath) const { 117 | TF_DEBUG(USD_URI_RESOLVER).Msg( 118 | "OpenAsset('%s')\n", resolvedPath.GetPathString()); 119 | std::shared_ptr asset; 120 | if (_OpenSqlAsset(resolvedPath.GetPathString(), asset)) { 121 | return asset; 122 | } 123 | return ArDefaultResolver::_OpenAsset(resolvedPath); 124 | } 125 | 126 | #else 127 | std::string URIResolver::Resolve(const std::string& path) { 128 | TF_DEBUG(USD_URI_RESOLVER).Msg("Resolve('%s')\n", path.c_str()); 129 | return URIResolver::ResolveWithAssetInfo(path, nullptr); 130 | } 131 | 132 | bool URIResolver::IsRelativePath(const std::string& path) { 133 | TF_DEBUG(USD_URI_RESOLVER).Msg("IsRelativePath('%s')\n", path.c_str()); 134 | return !SQL.matches_schema(path) && ArDefaultResolver::IsRelativePath(path); 135 | } 136 | 137 | std::string URIResolver::ResolveWithAssetInfo( 138 | const std::string& path, ArAssetInfo* assetInfo) { 139 | TF_DEBUG(USD_URI_RESOLVER) 140 | .Msg("ResolveWithAssetInfo('%s')\n", path.c_str()); 141 | std::string resolvedPath; 142 | if (_ResolveSql(path, resolvedPath)) { 143 | return resolvedPath; 144 | } 145 | return ArDefaultResolver::ResolveWithAssetInfo(path, assetInfo); 146 | } 147 | 148 | std::string URIResolver::AnchorRelativePath( 149 | const std::string& anchorPath, const std::string& path) { 150 | TF_DEBUG(USD_URI_RESOLVER) 151 | .Msg( 152 | "AnchorRelativePath('%s', '%s')\n", anchorPath.c_str(), 153 | path.c_str()); 154 | if (TfIsRelativePath(anchorPath) || !IsRelativePath(path)) { return path; } 155 | 156 | // Ensure we are using forward slashes and not back slashes. 157 | std::string forwardPath = anchorPath; 158 | std::replace(forwardPath.begin(), forwardPath.end(), '\\', '/'); 159 | 160 | // If anchorPath does not end with a '/', we assume it is specifying 161 | // a file, strip off the last component, and anchor the path to that 162 | // directory. 163 | const std::string anchoredPath = 164 | TfStringCatPaths(TfStringGetBeforeSuffix(forwardPath, '/'), path); 165 | return TfNormPath(anchoredPath); 166 | } 167 | 168 | VtValue URIResolver::GetModificationTimestamp( 169 | const std::string& path, const std::string& resolvedPath) { 170 | TF_DEBUG(USD_URI_RESOLVER) 171 | .Msg( 172 | "GetModificationTimestamp('%s', '%s')\n", path.c_str(), 173 | resolvedPath.c_str()); 174 | double timestamp; 175 | if (_GetTimestampSql(path, timestamp)) { 176 | return VtValue(timestamp); 177 | } 178 | return ArDefaultResolver::GetModificationTimestamp( 179 | path, resolvedPath); 180 | } 181 | 182 | bool URIResolver::FetchToLocalResolvedPath( 183 | const std::string& path, const std::string& resolvedPath) { 184 | TF_DEBUG(USD_URI_RESOLVER) 185 | .Msg( 186 | "FetchToLocalResolvedPath('%s', '%s')\n", path.c_str(), 187 | resolvedPath.c_str()); 188 | // We load the asset in OpenAsset. 189 | return !SQL.matches_schema(path) || 190 | ArDefaultResolver::FetchToLocalResolvedPath(path, resolvedPath); 191 | } 192 | 193 | std::shared_ptr URIResolver::OpenAsset( 194 | const std::string& resolvedPath) { 195 | TF_DEBUG(USD_URI_RESOLVER).Msg("OpenAsset('%s')\n", resolvedPath.c_str()); 196 | std::shared_ptr asset; 197 | if (_OpenSqlAsset(resolvedPath, asset)) { 198 | return asset; 199 | } 200 | return ArDefaultResolver::OpenAsset(resolvedPath); 201 | } 202 | #endif 203 | 204 | PXR_NAMESPACE_CLOSE_SCOPE 205 | -------------------------------------------------------------------------------- /external/z85/z85.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Stanislav Artemkin . 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are 6 | * met: 7 | * 8 | * * Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above 11 | * copyright notice, this list of conditions and the following disclaimer 12 | * in the documentation and/or other materials provided with the 13 | * distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 18 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 19 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 21 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | * 27 | * Implementation of 32/Z85 specification (http://rfc.zeromq.org/spec:32/Z85) 28 | * Source repository: http://github.com/artemkin/z85 29 | */ 30 | 31 | #pragma once 32 | 33 | #include 34 | 35 | #if defined (__cplusplus) 36 | extern "C" { 37 | #endif 38 | 39 | /******************************************************************************* 40 | * ZeroMQ Base-85 encoding/decoding functions with custom padding * 41 | *******************************************************************************/ 42 | 43 | /** 44 | * @brief Encodes 'inputSize' bytes from 'source' into 'dest'. 45 | * If 'inputSize' is not divisible by 4 with no remainder, 'source' is padded. 46 | * Destination buffer must be already allocated. Use Z85_encode_with_padding_bound() to 47 | * evaluate size of the destination buffer. 48 | * 49 | * @param source in, input buffer (binary string to be encoded) 50 | * @param dest out, destination buffer 51 | * @param inputSize in, number of bytes to be encoded 52 | * @return number of printable symbols written into 'dest' or 0 if something goes wrong 53 | */ 54 | size_t Z85_encode_with_padding(const char* source, char* dest, size_t inputSize); 55 | 56 | /** 57 | * @brief Decodes 'inputSize' printable symbols from 'source' into 'dest', 58 | * encoded with Z85_encode_with_padding(). 59 | * Destination buffer must be already allocated. Use Z85_decode_with_padding_bound() to 60 | * evaluate size of the destination buffer. 61 | * 62 | * @param source in, input buffer (printable string to be decoded) 63 | * @param dest out, destination buffer 64 | * @param inputSize in, number of symbols to be decoded 65 | * @return number of bytes written into 'dest' or 0 if something goes wrong 66 | */ 67 | size_t Z85_decode_with_padding(const char* source, char* dest, size_t inputSize); 68 | 69 | /** 70 | * @brief Evaluates a size of output buffer needed to encode 'size' bytes 71 | * into string of printable symbols using Z85_encode_with_padding(). 72 | * 73 | * @param size in, number of bytes to be encoded 74 | * @return minimal size of output buffer in bytes 75 | */ 76 | size_t Z85_encode_with_padding_bound(size_t size); 77 | 78 | /** 79 | * @brief Evaluates a size of output buffer needed to decode 'size' symbols 80 | * into binary string using Z85_decode_with_padding(). 81 | * 82 | * @param source in, input buffer (first symbol is read from 'source' to evaluate padding) 83 | * @param size in, number of symbols to be decoded 84 | * @return minimal size of output buffer in bytes 85 | */ 86 | size_t Z85_decode_with_padding_bound(const char* source, size_t size); 87 | 88 | 89 | 90 | /******************************************************************************* 91 | * ZeroMQ Base-85 encoding/decoding functions (specification compliant) * 92 | *******************************************************************************/ 93 | 94 | /** 95 | * @brief Encodes 'inputSize' bytes from 'source' into 'dest'. 96 | * If 'inputSize' is not divisible by 4 with no remainder, 0 is retured. 97 | * Destination buffer must be already allocated. Use Z85_encode_bound() to 98 | * evaluate size of the destination buffer. 99 | * 100 | * @param source in, input buffer (binary string to be encoded) 101 | * @param dest out, destination buffer 102 | * @param inputSize in, number of bytes to be encoded 103 | * @return number of printable symbols written into 'dest' or 0 if something goes wrong 104 | */ 105 | size_t Z85_encode(const char* source, char* dest, size_t inputSize); 106 | 107 | /** 108 | * @brief Decodes 'inputSize' printable symbols from 'source' into 'dest'. 109 | * If 'inputSize' is not divisible by 5 with no remainder, 0 is returned. 110 | * Destination buffer must be already allocated. Use Z85_decode_bound() to 111 | * evaluate size of the destination buffer. 112 | * 113 | * @param source in, input buffer (printable string to be decoded) 114 | * @param dest out, destination buffer 115 | * @param inputSize in, number of symbols to be decoded 116 | * @return number of bytes written into 'dest' or 0 if something goes wrong 117 | */ 118 | size_t Z85_decode(const char* source, char* dest, size_t inputSize); 119 | 120 | /** 121 | * @brief Evaluates a size of output buffer needed to encode 'size' bytes 122 | * into string of printable symbols using Z85_encode(). 123 | * 124 | * @param size in, number of bytes to be encoded 125 | * @return minimal size of output buffer in bytes 126 | */ 127 | size_t Z85_encode_bound(size_t size); 128 | 129 | /** 130 | * @brief Evaluates a size of output buffer needed to decode 'size' symbols 131 | * into binary string using Z85_decode(). 132 | * 133 | * @param size in, number of symbols to be decoded 134 | * @return minimal size of output buffer in bytes 135 | */ 136 | size_t Z85_decode_bound(size_t size); 137 | 138 | 139 | 140 | /******************************************************************************* 141 | * ZeroMQ Base-85 unsafe encoding/decoding functions (specification compliant) * 142 | *******************************************************************************/ 143 | 144 | /** 145 | * @brief Encodes bytes from [source;sourceEnd) range into 'dest'. 146 | * It can be used for implementation of your own padding scheme. 147 | * Preconditions: 148 | * - (sourceEnd - source) % 4 == 0 149 | * - destination buffer must be already allocated 150 | * 151 | * @param source in, begin of input buffer 152 | * @param sourceEnd in, end of input buffer (not included) 153 | * @param dest out, output buffer 154 | * @return a pointer immediately after last symbol written into the 'dest' 155 | */ 156 | char* Z85_encode_unsafe(const char* source, const char* sourceEnd, char* dest); 157 | 158 | /** 159 | * @brief Decodes symbols from [source;sourceEnd) range into 'dest'. 160 | * It can be used for implementation of your own padding scheme. 161 | * Preconditions: 162 | * - (sourceEnd - source) % 5 == 0 163 | * - destination buffer must be already allocated 164 | * 165 | * @param source in, begin of input buffer 166 | * @param sourceEnd in, end of input buffer (not included) 167 | * @param dest out, output buffer 168 | * @return a pointer immediately after last byte written into the 'dest' 169 | */ 170 | char* Z85_decode_unsafe(const char* source, const char* sourceEnd, char* dest); 171 | 172 | #if defined (__cplusplus) 173 | } 174 | #endif 175 | 176 | -------------------------------------------------------------------------------- /external/z85/z85.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Stanislav Artemkin . 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are 6 | * met: 7 | * 8 | * * Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above 11 | * copyright notice, this list of conditions and the following disclaimer 12 | * in the documentation and/or other materials provided with the 13 | * distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 18 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 19 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 21 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | * 27 | * Implementation of 32/Z85 specification (http://rfc.zeromq.org/spec:32/Z85) 28 | * Source repository: http://github.com/artemkin/z85 29 | */ 30 | 31 | #include 32 | #include 33 | 34 | #include "z85.h" 35 | 36 | typedef unsigned int uint32_t; 37 | typedef unsigned char byte; 38 | 39 | // make sure uint32_t is 32-bit 40 | typedef char Z85_uint32_t_static_assert[(sizeof(uint32_t) * CHAR_BIT == 32) * 2 - 1]; 41 | 42 | #define DIV85_MAGIC 3233857729ULL 43 | // make sure magic constant is 64-bit 44 | typedef char Z85_div85_magic_static_assert[(sizeof(DIV85_MAGIC) * CHAR_BIT == 64) * 2 - 1]; 45 | 46 | #define DIV85(number) ((uint32_t)((DIV85_MAGIC * number) >> 32) >> 6) 47 | 48 | static const char* base85 = 49 | { 50 | "0123456789" 51 | "abcdefghij" 52 | "klmnopqrst" 53 | "uvwxyzABCD" 54 | "EFGHIJKLMN" 55 | "OPQRSTUVWX" 56 | "YZ.-:+=^!/" 57 | "*?&<>()[]{" 58 | "}@%$#" 59 | }; 60 | 61 | static byte base256[] = 62 | { 63 | 0x00, 0x44, 0x00, 0x54, 0x53, 0x52, 0x48, 0x00, 64 | 0x4B, 0x4C, 0x46, 0x41, 0x00, 0x3F, 0x3E, 0x45, 65 | 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 66 | 0x08, 0x09, 0x40, 0x00, 0x49, 0x42, 0x4A, 0x47, 67 | 0x51, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 68 | 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 69 | 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 70 | 0x3B, 0x3C, 0x3D, 0x4D, 0x00, 0x4E, 0x43, 0x00, 71 | 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 72 | 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 73 | 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 74 | 0x21, 0x22, 0x23, 0x4F, 0x00, 0x50, 0x00, 0x00 75 | }; 76 | 77 | char* Z85_encode_unsafe(const char* source, const char* sourceEnd, char* dest) 78 | { 79 | byte* src = (byte*)source; 80 | byte* end = (byte*)sourceEnd; 81 | byte* dst = (byte*)dest; 82 | uint32_t value; 83 | uint32_t value2; 84 | 85 | for (; src != end; src += 4, dst += 5) 86 | { 87 | // unpack big-endian frame 88 | value = (src[0] << 24) | (src[1] << 16) | (src[2] << 8) | src[3]; 89 | 90 | value2 = DIV85(value); dst[4] = base85[value - value2 * 85]; value = value2; 91 | value2 = DIV85(value); dst[3] = base85[value - value2 * 85]; value = value2; 92 | value2 = DIV85(value); dst[2] = base85[value - value2 * 85]; value = value2; 93 | value2 = DIV85(value); dst[1] = base85[value - value2 * 85]; 94 | dst[0] = base85[value2]; 95 | } 96 | 97 | return (char*)dst; 98 | } 99 | 100 | char* Z85_decode_unsafe(const char* source, const char* sourceEnd, char* dest) 101 | { 102 | byte* src = (byte*)source; 103 | byte* end = (byte*)sourceEnd; 104 | byte* dst = (byte*)dest; 105 | uint32_t value; 106 | 107 | for (; src != end; src += 5, dst += 4) 108 | { 109 | value = base256[(src[0] - 32) & 127]; 110 | value = value * 85 + base256[(src[1] - 32) & 127]; 111 | value = value * 85 + base256[(src[2] - 32) & 127]; 112 | value = value * 85 + base256[(src[3] - 32) & 127]; 113 | value = value * 85 + base256[(src[4] - 32) & 127]; 114 | 115 | // pack big-endian frame 116 | dst[0] = value >> 24; 117 | dst[1] = (byte)(value >> 16); 118 | dst[2] = (byte)(value >> 8); 119 | dst[3] = (byte)(value); 120 | } 121 | 122 | return (char*)dst; 123 | } 124 | 125 | size_t Z85_encode_bound(size_t size) 126 | { 127 | return size * 5 / 4; 128 | } 129 | 130 | size_t Z85_decode_bound(size_t size) 131 | { 132 | return size * 4 / 5; 133 | } 134 | 135 | size_t Z85_encode(const char* source, char* dest, size_t inputSize) 136 | { 137 | if (!source || !dest || inputSize % 4) 138 | { 139 | assert(!"wrong source, destination or input size"); 140 | return 0; 141 | } 142 | 143 | return Z85_encode_unsafe(source, source + inputSize, dest) - dest; 144 | } 145 | 146 | size_t Z85_decode(const char* source, char* dest, size_t inputSize) 147 | { 148 | if (!source || !dest || inputSize % 5) 149 | { 150 | assert(!"wrong source, destination or input size"); 151 | return 0; 152 | } 153 | 154 | return Z85_decode_unsafe(source, source + inputSize, dest) - dest; 155 | } 156 | 157 | size_t Z85_encode_with_padding_bound(size_t size) 158 | { 159 | if (size == 0) return 0; 160 | size = Z85_encode_bound(size); 161 | return size + (5 - size % 5) % 5 + 1; 162 | } 163 | 164 | size_t Z85_decode_with_padding_bound(const char* source, size_t size) 165 | { 166 | if (size == 0 || !source || (byte)(source[0] - '0' - 1) > 3) return 0; 167 | return Z85_decode_bound(size - 1) - 4 + (source[0] - '0'); 168 | } 169 | 170 | size_t Z85_encode_with_padding(const char* source, char* dest, size_t inputSize) 171 | { 172 | size_t tailBytes = inputSize % 4; 173 | char tailBuf[4] = { 0 }; 174 | char* dst = dest; 175 | const char* end = source + inputSize - tailBytes; 176 | 177 | assert(source && dest); 178 | 179 | // zero length string is not padded 180 | if (!source || !dest || inputSize == 0) 181 | { 182 | return 0; 183 | } 184 | 185 | (dst++)[0] = (tailBytes == 0 ? '4' : '0' + (char)tailBytes); // write tail bytes count 186 | dst = Z85_encode_unsafe(source, end, dst); // write body 187 | 188 | // write tail 189 | switch (tailBytes) 190 | { 191 | case 3: 192 | tailBuf[2] = end[2]; 193 | case 2: 194 | tailBuf[1] = end[1]; 195 | case 1: 196 | tailBuf[0] = end[0]; 197 | dst = Z85_encode_unsafe(tailBuf, tailBuf + 4, dst); 198 | } 199 | 200 | return dst - dest; 201 | } 202 | 203 | size_t Z85_decode_with_padding(const char* source, char* dest, size_t inputSize) 204 | { 205 | char* dst = dest; 206 | size_t tailBytes; 207 | char tailBuf[4] = { 0 }; 208 | const char* end = source + inputSize; 209 | 210 | assert(source && dest && (inputSize == 0 || (inputSize - 1) % 5 == 0)); 211 | 212 | // zero length string is not padded 213 | if (!source || !dest || inputSize == 0 || (inputSize - 1) % 5) 214 | { 215 | return 0; 216 | } 217 | 218 | tailBytes = (source++)[0] - '0'; // possible values: 1, 2, 3 or 4 219 | if (tailBytes - 1 > 3) 220 | { 221 | assert(!"wrong tail bytes count"); 222 | return 0; 223 | } 224 | 225 | end -= 5; 226 | if (source != end) 227 | { 228 | // decode body 229 | dst = Z85_decode_unsafe(source, end, dst); 230 | } 231 | 232 | // decode last 5 bytes chunk 233 | Z85_decode_unsafe(end, end + 5, tailBuf); 234 | 235 | switch (tailBytes) 236 | { 237 | case 4: 238 | dst[3] = tailBuf[3]; 239 | case 3: 240 | dst[2] = tailBuf[2]; 241 | case 2: 242 | dst[1] = tailBuf[1]; 243 | case 1: 244 | dst[0] = tailBuf[0]; 245 | } 246 | 247 | return dst - dest + tailBytes; 248 | } 249 | -------------------------------------------------------------------------------- /cmake/FindUSD.cmake: -------------------------------------------------------------------------------- 1 | # Simple module to find USD. 2 | 3 | if (WIN32) 4 | # On Windows we need to find ".lib"... which is CMAKE_STATIC_LIBRARY_SUFFIX 5 | # on WIN32 (CMAKE_SHARED_LIBRARY_SUFFIX is ".dll") 6 | set(USD_LIB_SUFFIX ${CMAKE_STATIC_LIBRARY_SUFFIX} 7 | CACHE STRING "Extension of USD libraries") 8 | else () 9 | # Defaults to ".so" on Linux, ".dylib" on MacOS 10 | set(USD_LIB_SUFFIX ${CMAKE_SHARED_LIBRARY_SUFFIX} 11 | CACHE STRING "Extension of USD libraries") 12 | endif () 13 | 14 | # Note: for USD <= 0.19.11, there was a bug where, regardless of what 15 | # PXR_LIB_PREFIX was set to, the behavior on windows was that the .lib files 16 | # ALWAYS had no prefix. However, the PXR_LIB_PREFIX - which defaulted to "lib", 17 | # even on windows - WAS used for the .dll names. 18 | # 19 | # So, if PXR_LIB_PREFIX was left at it's default value of "lib", you 20 | # had output libs like: 21 | # usd.lib 22 | # libusd.dll 23 | # 24 | # The upshot is that, for windows and USD <= 0.19.11, you probably want to 25 | # leave USD_LIB_PREFIX at it's default (empty string on windows), even if you 26 | # set a PXR_LIB_PREFIX when building USD core. 27 | 28 | set(USD_LIB_PREFIX ${CMAKE_SHARED_LIBRARY_PREFIX} 29 | CACHE STRING "Prefix of USD libraries") 30 | 31 | find_path(USD_INCLUDE_DIR pxr/pxr.h 32 | HINTS 33 | ${USD_ROOT}/include 34 | $ENV{USD_ROOT}/include 35 | DOC "USD Include directory") 36 | 37 | # Disabled because this FindUSD doesn't work with pxrConfig.cmake - see note 38 | # below 39 | 40 | # find_file(USD_CONFIG_FILE 41 | # names pxrConfig.cmake 42 | # PATHS ${USD_ROOT} 43 | # $ENV{USD_ROOT} 44 | # DOC "USD cmake configuration file") 45 | 46 | # We need to find either usd or usd_ms (the monolithic-shared library), 47 | # with taking the prefix into account. 48 | find_path(USD_LIBRARY_DIR 49 | NAMES 50 | ${USD_LIB_PREFIX}usd${USD_LIB_SUFFIX} 51 | ${USD_LIB_PREFIX}usd_ms${USD_LIB_SUFFIX} 52 | HINTS 53 | ${USD_ROOT}/lib 54 | $ENV{USD_ROOT}/lib 55 | DOC "USD Libraries directory") 56 | 57 | find_file(USD_GENSCHEMA 58 | NAMES usdGenSchema 59 | HINTS 60 | ${USD_ROOT} 61 | $ENV{USD_ROOT} 62 | PATH_SUFFIXES 63 | bin 64 | DOC "USD Gen schema application") 65 | 66 | # USD Maya components 67 | 68 | find_path(USD_MAYA_INCLUDE_DIR usdMaya/api.h 69 | HINTS 70 | # If we're using Autodesk Maya-USD repo 71 | ${MAYA_USD_ROOT}/plugin/pxr/maya/include 72 | $ENV{MAYA_USD_ROOT}/plugin/pxr/maya/include 73 | 74 | # If we're using Pixar USD core repo (<=0.19.11) 75 | ${USD_ROOT}/third_party/maya/include 76 | $ENV{USD_ROOT}/third_party/maya/include 77 | ${USD_MAYA_ROOT}/third_party/maya/include 78 | $ENV{USD_MAYA_ROOT}/third_party/maya/include 79 | DOC "USD Maya Include directory") 80 | 81 | find_path(USD_MAYA_LIBRARY_DIR 82 | NAMES 83 | # If we're using Autodesk Maya-USD repo 84 | ${CMAKE_SHARED_LIBRARY_PREFIX}usdMaya${USD_LIB_SUFFIX} 85 | 86 | # If we're using Pixar USD core repo (<=0.19.11) 87 | ${USD_LIB_PREFIX}usdMaya${USD_LIB_SUFFIX} 88 | HINTS 89 | # If we're using Autodesk Maya-USD repo 90 | ${MAYA_USD_ROOT}/plugin/pxr/maya/lib 91 | $ENV{MAYA_USD_ROOT}/plugin/pxr/maya/lib 92 | 93 | # If we're using Pixar USD core repo (<=0.19.11) 94 | ${USD_ROOT}/third_party/maya/lib 95 | $ENV{USD_ROOT}/third_party/maya/lib 96 | ${USD_MAYA_ROOT}/third_party/maya/lib 97 | $ENV{USD_MAYA_ROOT}/third_party/maya/lib 98 | DOC "USD Maya Library directory") 99 | 100 | # USD Katana components 101 | 102 | find_path(USD_KATANA_INCLUDE_DIR usdKatana/api.h 103 | HINTS 104 | ${USD_ROOT}/third_party/katana/include 105 | $ENV{USD_ROOT}/third_party/katana/include 106 | ${USD_KATANA_ROOT}/third_party/katana/include 107 | $ENV{USD_KATANA_ROOT}/third_party/katana/include 108 | DOC "USD Katana Include directory") 109 | 110 | find_path(USD_KATANA_LIBRARY_DIR 111 | NAMES 112 | ${USD_LIB_PREFIX}usdKatana${USD_LIB_SUFFIX} 113 | HINTS 114 | ${USD_ROOT}/third_party/katana/lib 115 | $ENV{USD_ROOT}/third_party/katana/lib 116 | ${USD_KATANA_ROOT}/third_party/katana/lib 117 | $ENV{USD_KATANA_ROOT}/third_party/katana/lib 118 | DOC "USD Katana Library directory") 119 | 120 | # USD Houdini components 121 | 122 | find_path(USD_HOUDINI_INCLUDE_DIR gusd/api.h 123 | HINTS 124 | ${USD_ROOT}/third_party/houdini/include 125 | $ENV{USD_ROOT}/third_party/houdini/include 126 | ${USD_HOUDINI_ROOT}/third_party/houdini/include 127 | $ENV{USD_HOUDINI_ROOT}/third_party/houdini/include 128 | DOC "USD Houdini Include directory") 129 | 130 | find_path(USD_HOUDINI_LIBRARY_DIR 131 | NAMES 132 | ${USD_LIB_PREFIX}gusd${USD_LIB_SUFFIX} 133 | HINTS 134 | ${USD_ROOT}/third_party/houdini/lib 135 | $ENV{USD_ROOT}/third_party/houdini/lib 136 | ${USD_HOUDINI_ROOT}/third_party/houdini/lib 137 | $ENV{USD_HOUDINI_ROOT}/third_party/houdini/lib 138 | DOC "USD Houdini Library directory") 139 | 140 | if(USD_INCLUDE_DIR AND EXISTS "${USD_INCLUDE_DIR}/pxr/pxr.h") 141 | foreach(_usd_comp MAJOR MINOR PATCH) 142 | file(STRINGS 143 | "${USD_INCLUDE_DIR}/pxr/pxr.h" 144 | _usd_tmp 145 | REGEX "#define PXR_${_usd_comp}_VERSION .*$") 146 | string(REGEX MATCHALL "[0-9]+" USD_${_usd_comp}_VERSION ${_usd_tmp}) 147 | endforeach() 148 | set(USD_VERSION ${USD_MAJOR_VERSION}.${USD_MINOR_VERSION}.${USD_PATCH_VERSION}) 149 | endif() 150 | 151 | # NOTE: setting the usd libs to be INTERFACE IMPORTED targets conflicts with 152 | # usage of pxrConfig.cmake - so if you are using this FindUSD, you 153 | # currently can't use pxrConfig.cmake. You could comment out / remove 154 | # these sections to allow usage of pxrConfig.cmake. 155 | # We considered using pxrConfig.cmake, but we don't like the fact that it 156 | # bakes in full paths to the various dependencies 157 | 158 | set(USD_LIBS ar;arch;cameraUtil;garch;gf;glf;hd;hdSt;hdx;hf;hgi;hgiGL;hio;js;kind;ndr;pcp;plug;pxOsd;sdf;sdr;tf;trace;usd;usdAppUtils;usdGeom;usdHydra;usdImaging;usdImagingGL;usdLux;usdRender;usdRi;usdShade;usdShaders;usdSkel;usdSkelImaging;usdUI;usdUtils;usdviewq;usdVol;usdVolImaging;vt;work;usd_ms) 159 | 160 | foreach (lib ${USD_LIBS}) 161 | find_library(USD_${lib}_LIBRARY 162 | NAMES ${USD_LIB_PREFIX}${lib}${USD_LIB_SUFFIX} 163 | HINTS ${USD_LIBRARY_DIR}) 164 | if (USD_${lib}_LIBRARY) 165 | add_library(${lib} INTERFACE IMPORTED) 166 | set_target_properties(${lib} 167 | PROPERTIES 168 | INTERFACE_LINK_LIBRARIES ${USD_${lib}_LIBRARY} 169 | ) 170 | list(APPEND USD_LIBRARIES ${USD_${lib}_LIBRARY}) 171 | endif () 172 | endforeach () 173 | 174 | set(USD_MAYA_LIBS px_vp20;pxrUsdMayaGL;usdMaya) 175 | 176 | foreach (lib ${USD_MAYA_LIBS}) 177 | find_library(USD_MAYA_${lib}_LIBRARY 178 | NAMES 179 | # If we're using Autodesk Maya-USD repo 180 | ${lib}${USD_LIB_SUFFIX} 181 | 182 | # If we're using Pixar USD core repo (<=0.19.11) 183 | ${USD_LIB_PREFIX}${lib}${USD_LIB_SUFFIX} 184 | HINTS ${USD_MAYA_LIBRARY_DIR}) 185 | if (USD_MAYA_${lib}_LIBRARY) 186 | add_library(${lib} INTERFACE IMPORTED) 187 | set_target_properties(${lib} 188 | PROPERTIES 189 | INTERFACE_LINK_LIBRARIES ${USD_MAYA_${lib}_LIBRARY} 190 | ) 191 | list(APPEND USD_MAYA_LIBRARIES ${USD_MAYA_${lib}_LIBRARY}) 192 | endif () 193 | endforeach () 194 | 195 | set(USD_KATANA_LIBS usdKatana;vtKatana) 196 | 197 | foreach (lib ${USD_KATANA_LIBS}) 198 | find_library(USD_KATANA_${lib}_LIBRARY 199 | NAMES ${USD_LIB_PREFIX}${lib}${USD_LIB_SUFFIX} 200 | HINTS ${USD_KATANA_LIBRARY_DIR}) 201 | if (USD_KATANA_${lib}_LIBRARY) 202 | add_library(${lib} INTERFACE IMPORTED) 203 | set_target_properties(${lib} 204 | PROPERTIES 205 | INTERFACE_LINK_LIBRARIES ${USD_KATANA_${lib}_LIBRARY} 206 | ) 207 | list(APPEND USD_KATANA_LIBRARIES ${USD_KATANA_${lib}_LIBRARY}) 208 | endif () 209 | endforeach () 210 | 211 | include(FindPackageHandleStandardArgs) 212 | 213 | find_package_handle_standard_args(USD 214 | REQUIRED_VARS 215 | USD_INCLUDE_DIR 216 | USD_LIBRARY_DIR 217 | USD_LIBRARIES 218 | VERSION_VAR 219 | USD_VERSION) 220 | -------------------------------------------------------------------------------- /cmake/FindTBB.cmake: -------------------------------------------------------------------------------- 1 | 2 | # The MIT License (MIT) 3 | # 4 | # Copyright (c) 2015 Justus Calvin 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in all 14 | # copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | # SOFTWARE. 23 | 24 | # 25 | # FindTBB 26 | # ------- 27 | # 28 | # Find TBB include directories and libraries. 29 | # 30 | # Usage: 31 | # 32 | # find_package(TBB [major[.minor]] [EXACT] 33 | # [QUIET] [REQUIRED] 34 | # [[COMPONENTS] [components...]] 35 | # [OPTIONAL_COMPONENTS components...]) 36 | # 37 | # where the allowed components are tbbmalloc and tbb_preview. Users may modify 38 | # the behavior of this module with the following variables: 39 | # 40 | # * TBB_ROOT_DIR - The base directory the of TBB installation. 41 | # * TBB_INCLUDE_DIR - The directory that contains the TBB headers files. 42 | # * TBB_LIBRARY - The directory that contains the TBB library files. 43 | # * TBB__LIBRARY - The path of the TBB the corresponding TBB library. 44 | # These libraries, if specified, override the 45 | # corresponding library search results, where 46 | # may be tbb, tbb_debug, tbbmalloc, tbbmalloc_debug, 47 | # tbb_preview, or tbb_preview_debug. 48 | # * TBB_USE_DEBUG_BUILD - The debug version of tbb libraries, if present, will 49 | # be used instead of the release version. 50 | # 51 | # Users may modify the behavior of this module with the following environment 52 | # variables: 53 | # 54 | # * TBB_INSTALL_DIR 55 | # * TBBROOT 56 | # * LIBRARY_PATH 57 | # 58 | # This module will set the following variables: 59 | # 60 | # * TBB_FOUND - Set to false, or undefined, if we haven’t found, or 61 | # don’t want to use TBB. 62 | # * TBB__FOUND - If False, optional part of TBB sytem is 63 | # not available. 64 | # * TBB_VERSION - The full version string 65 | # * TBB_VERSION_MAJOR - The major version 66 | # * TBB_VERSION_MINOR - The minor version 67 | # * TBB_INTERFACE_VERSION - The interface version number defined in 68 | # tbb/tbb_stddef.h. 69 | # * TBB__LIBRARY_RELEASE - The path of the TBB release version of 70 | # , where may be tbb, tbb_debug, 71 | # tbbmalloc, tbbmalloc_debug, tbb_preview, or 72 | # tbb_preview_debug. 73 | # * TBB__LIBRARY_DEGUG - The path of the TBB release version of 74 | # , where may be tbb, tbb_debug, 75 | # tbbmalloc, tbbmalloc_debug, tbb_preview, or 76 | # tbb_preview_debug. 77 | # 78 | # The following varibles should be used to build and link with TBB: 79 | # 80 | # * TBB_INCLUDE_DIRS - The include directory for TBB. 81 | # * TBB_LIBRARIES - The libraries to link against to use TBB. 82 | # * TBB_LIBRARIES_RELEASE - The release libraries to link against to use TBB. 83 | # * TBB_LIBRARIES_DEBUG - The debug libraries to link against to use TBB. 84 | # * TBB_DEFINITIONS - Definitions to use when compiling code that uses 85 | # TBB. 86 | # * TBB_DEFINITIONS_RELEASE - Definitions to use when compiling release code that 87 | # uses TBB. 88 | # * TBB_DEFINITIONS_DEBUG - Definitions to use when compiling debug code that 89 | # uses TBB. 90 | # 91 | # This module will also create the "tbb" target that may be used when building 92 | # executables and libraries. 93 | 94 | include(FindPackageHandleStandardArgs) 95 | 96 | if(NOT TBB_FOUND) 97 | 98 | ################################## 99 | # Check the build type 100 | ################################## 101 | 102 | if(NOT DEFINED TBB_USE_DEBUG_BUILD) 103 | if(CMAKE_BUILD_TYPE MATCHES "(Debug|DEBUG|debug|RelWithDebInfo|RELWITHDEBINFO|relwithdebinfo)") 104 | set(TBB_BUILD_TYPE DEBUG) 105 | else() 106 | set(TBB_BUILD_TYPE RELEASE) 107 | endif() 108 | elseif(TBB_USE_DEBUG_BUILD) 109 | set(TBB_BUILD_TYPE DEBUG) 110 | else() 111 | set(TBB_BUILD_TYPE RELEASE) 112 | endif() 113 | 114 | ################################## 115 | # Set the TBB search directories 116 | ################################## 117 | 118 | # Define search paths based on user input and environment variables 119 | set(TBB_SEARCH_DIR ${TBB_ROOT_DIR} $ENV{TBB_INSTALL_DIR} $ENV{TBBROOT}) 120 | 121 | # Define the search directories based on the current platform 122 | if(CMAKE_SYSTEM_NAME STREQUAL "Windows") 123 | set(TBB_DEFAULT_SEARCH_DIR "C:/Program Files/Intel/TBB" 124 | "C:/Program Files (x86)/Intel/TBB") 125 | 126 | # Set the target architecture 127 | if(CMAKE_SIZEOF_VOID_P EQUAL 8) 128 | set(TBB_ARCHITECTURE "intel64") 129 | else() 130 | set(TBB_ARCHITECTURE "ia32") 131 | endif() 132 | 133 | # Set the TBB search library path search suffix based on the version of VC 134 | if(WINDOWS_STORE) 135 | set(TBB_LIB_PATH_SUFFIX "lib/${TBB_ARCHITECTURE}/vc11_ui") 136 | elseif(MSVC14) 137 | set(TBB_LIB_PATH_SUFFIX "lib/${TBB_ARCHITECTURE}/vc14") 138 | elseif(MSVC12) 139 | set(TBB_LIB_PATH_SUFFIX "lib/${TBB_ARCHITECTURE}/vc12") 140 | elseif(MSVC11) 141 | set(TBB_LIB_PATH_SUFFIX "lib/${TBB_ARCHITECTURE}/vc11") 142 | elseif(MSVC10) 143 | set(TBB_LIB_PATH_SUFFIX "lib/${TBB_ARCHITECTURE}/vc10") 144 | endif() 145 | 146 | # Add the library path search suffix for the VC independent version of TBB 147 | list(APPEND TBB_LIB_PATH_SUFFIX "lib/${TBB_ARCHITECTURE}/vc_mt") 148 | 149 | elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin") 150 | # OS X 151 | set(TBB_DEFAULT_SEARCH_DIR "/opt/intel/tbb") 152 | 153 | # TODO: Check to see which C++ library is being used by the compiler. 154 | if(NOT ${CMAKE_SYSTEM_VERSION} VERSION_LESS 13.0) 155 | # The default C++ library on OS X 10.9 and later is libc++ 156 | set(TBB_LIB_PATH_SUFFIX "lib/libc++" "lib") 157 | else() 158 | set(TBB_LIB_PATH_SUFFIX "lib") 159 | endif() 160 | elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux") 161 | # Linux 162 | set(TBB_DEFAULT_SEARCH_DIR "/opt/intel/tbb") 163 | 164 | # TODO: Check compiler version to see the suffix should be /gcc4.1 or 165 | # /gcc4.1. For now, assume that the compiler is more recent than 166 | # gcc 4.4.x or later. 167 | if(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64") 168 | set(TBB_LIB_PATH_SUFFIX "lib/intel64/gcc4.4") 169 | elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^i.86$") 170 | set(TBB_LIB_PATH_SUFFIX "lib/ia32/gcc4.4") 171 | endif() 172 | endif() 173 | 174 | ################################## 175 | # Find the TBB include dir 176 | ################################## 177 | 178 | find_path(TBB_INCLUDE_DIRS tbb/tbb.h 179 | HINTS ${TBB_INCLUDE_DIR} ${TBB_SEARCH_DIR} 180 | PATHS ${TBB_DEFAULT_SEARCH_DIR} 181 | PATH_SUFFIXES include) 182 | 183 | ################################## 184 | # Set version strings 185 | ################################## 186 | 187 | if(TBB_INCLUDE_DIRS) 188 | file(READ "${TBB_INCLUDE_DIRS}/tbb/tbb_stddef.h" _tbb_version_file) 189 | string(REGEX REPLACE ".*#define TBB_VERSION_MAJOR ([0-9]+).*" "\\1" 190 | TBB_VERSION_MAJOR "${_tbb_version_file}") 191 | string(REGEX REPLACE ".*#define TBB_VERSION_MINOR ([0-9]+).*" "\\1" 192 | TBB_VERSION_MINOR "${_tbb_version_file}") 193 | string(REGEX REPLACE ".*#define TBB_INTERFACE_VERSION ([0-9]+).*" "\\1" 194 | TBB_INTERFACE_VERSION "${_tbb_version_file}") 195 | set(TBB_VERSION "${TBB_VERSION_MAJOR}.${TBB_VERSION_MINOR}") 196 | endif() 197 | 198 | ################################## 199 | # Find TBB components 200 | ################################## 201 | 202 | if(TBB_VERSION VERSION_LESS 4.3) 203 | set(TBB_SEARCH_COMPOMPONENTS tbb_preview tbbmalloc tbb) 204 | else() 205 | set(TBB_SEARCH_COMPOMPONENTS tbb_preview tbbmalloc_proxy tbbmalloc tbb) 206 | endif() 207 | 208 | # Find each component 209 | foreach(_comp ${TBB_SEARCH_COMPOMPONENTS}) 210 | if(";${TBB_FIND_COMPONENTS};tbb;" MATCHES ";${_comp};") 211 | 212 | # Search for the libraries 213 | find_library(TBB_${_comp}_LIBRARY_RELEASE ${_comp} 214 | HINTS ${TBB_LIBRARY} ${TBB_SEARCH_DIR} 215 | PATHS ${TBB_DEFAULT_SEARCH_DIR} ENV LIBRARY_PATH 216 | PATH_SUFFIXES ${TBB_LIB_PATH_SUFFIX}) 217 | 218 | find_library(TBB_${_comp}_LIBRARY_DEBUG ${_comp}_debug 219 | HINTS ${TBB_LIBRARY} ${TBB_SEARCH_DIR} 220 | PATHS ${TBB_DEFAULT_SEARCH_DIR} ENV LIBRARY_PATH 221 | PATH_SUFFIXES ${TBB_LIB_PATH_SUFFIX}) 222 | 223 | if(TBB_${_comp}_LIBRARY_DEBUG) 224 | list(APPEND TBB_LIBRARIES_DEBUG "${TBB_${_comp}_LIBRARY_DEBUG}") 225 | endif() 226 | if(TBB_${_comp}_LIBRARY_RELEASE) 227 | list(APPEND TBB_LIBRARIES_RELEASE "${TBB_${_comp}_LIBRARY_RELEASE}") 228 | endif() 229 | if(TBB_${_comp}_LIBRARY_${TBB_BUILD_TYPE} AND NOT TBB_${_comp}_LIBRARY) 230 | set(TBB_${_comp}_LIBRARY "${TBB_${_comp}_LIBRARY_${TBB_BUILD_TYPE}}") 231 | endif() 232 | 233 | if(TBB_${_comp}_LIBRARY AND EXISTS "${TBB_${_comp}_LIBRARY}") 234 | set(TBB_${_comp}_FOUND TRUE) 235 | else() 236 | set(TBB_${_comp}_FOUND FALSE) 237 | endif() 238 | 239 | # Mark internal variables as advanced 240 | mark_as_advanced(TBB_${_comp}_LIBRARY_RELEASE) 241 | mark_as_advanced(TBB_${_comp}_LIBRARY_DEBUG) 242 | mark_as_advanced(TBB_${_comp}_LIBRARY) 243 | 244 | endif() 245 | endforeach() 246 | 247 | ################################## 248 | # Set compile flags and libraries 249 | ################################## 250 | 251 | set(TBB_DEFINITIONS_RELEASE "") 252 | set(TBB_DEFINITIONS_DEBUG "-DTBB_USE_DEBUG=1") 253 | 254 | if(TBB_LIBRARIES_${TBB_BUILD_TYPE}) 255 | set(TBB_DEFINITIONS "${TBB_DEFINITIONS_${TBB_BUILD_TYPE}}") 256 | set(TBB_LIBRARIES "${TBB_LIBRARIES_${TBB_BUILD_TYPE}}") 257 | elseif(TBB_LIBRARIES_RELEASE) 258 | set(TBB_DEFINITIONS "${TBB_DEFINITIONS_RELEASE}") 259 | set(TBB_LIBRARIES "${TBB_LIBRARIES_RELEASE}") 260 | elseif(TBB_LIBRARIES_DEBUG) 261 | set(TBB_DEFINITIONS "${TBB_DEFINITIONS_DEBUG}") 262 | set(TBB_LIBRARIES "${TBB_LIBRARIES_DEBUG}") 263 | endif() 264 | 265 | find_package_handle_standard_args(TBB 266 | REQUIRED_VARS TBB_INCLUDE_DIRS TBB_LIBRARIES 267 | HANDLE_COMPONENTS 268 | VERSION_VAR TBB_VERSION) 269 | 270 | ################################## 271 | # Create targets 272 | ################################## 273 | 274 | if(NOT CMAKE_VERSION VERSION_LESS 3.0 AND TBB_FOUND) 275 | add_library(tbb SHARED IMPORTED) 276 | set_target_properties(tbb PROPERTIES 277 | INTERFACE_INCLUDE_DIRECTORIES ${TBB_INCLUDE_DIRS} 278 | IMPORTED_LOCATION ${TBB_LIBRARIES}) 279 | if(TBB_LIBRARIES_RELEASE AND TBB_LIBRARIES_DEBUG) 280 | set_target_properties(tbb PROPERTIES 281 | INTERFACE_COMPILE_DEFINITIONS "$<$,$>:TBB_USE_DEBUG=1>" 282 | IMPORTED_LOCATION_DEBUG ${TBB_LIBRARIES_DEBUG} 283 | IMPORTED_LOCATION_RELWITHDEBINFO ${TBB_LIBRARIES_DEBUG} 284 | IMPORTED_LOCATION_RELEASE ${TBB_LIBRARIES_RELEASE} 285 | IMPORTED_LOCATION_MINSIZEREL ${TBB_LIBRARIES_RELEASE} 286 | ) 287 | elseif(TBB_LIBRARIES_RELEASE) 288 | set_target_properties(tbb PROPERTIES IMPORTED_LOCATION ${TBB_LIBRARIES_RELEASE}) 289 | else() 290 | set_target_properties(tbb PROPERTIES 291 | INTERFACE_COMPILE_DEFINITIONS "${TBB_DEFINITIONS_DEBUG}" 292 | IMPORTED_LOCATION ${TBB_LIBRARIES_DEBUG} 293 | ) 294 | endif() 295 | endif() 296 | 297 | mark_as_advanced(TBB_INCLUDE_DIRS TBB_LIBRARIES) 298 | 299 | unset(TBB_ARCHITECTURE) 300 | unset(TBB_BUILD_TYPE) 301 | unset(TBB_LIB_PATH_SUFFIX) 302 | unset(TBB_DEFAULT_SEARCH_DIR) 303 | 304 | endif() -------------------------------------------------------------------------------- /URIResolver/sql.cpp: -------------------------------------------------------------------------------- 1 | #include "sql.h" 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | 17 | #include "debug_codes.h" 18 | #include "memory_asset.h" 19 | 20 | PXR_NAMESPACE_OPEN_SCOPE 21 | 22 | // ----------------------------------------------------------------------------- 23 | 24 | namespace { 25 | 26 | constexpr auto SQL_PREFIX = "sql://"; 27 | constexpr auto SQL_PREFIX_SHORT = "sql:"; 28 | constexpr auto HOST_ENV_VAR = "USD_SQL_DBHOST"; 29 | constexpr auto PORT_ENV_VAR = "USD_SQL_PORT"; 30 | constexpr auto DB_ENV_VAR = "USD_SQL_DB"; 31 | constexpr auto TABLE_ENV_VAR = "USD_SQL_TABLE"; 32 | constexpr auto USER_ENV_VAR = "USD_SQL_USER"; 33 | constexpr auto PASSWORD_ENV_VAR = "USD_SQL_PASSWD"; 34 | 35 | constexpr double INVALID_TIME = std::numeric_limits::lowest(); 36 | 37 | struct MySQLResultDeleter { 38 | void operator()(MYSQL_RES* r) const { mysql_free_result(r); } 39 | }; 40 | using MySQLResult = std::unique_ptr; 41 | 42 | using mutex_scoped_lock = std::lock_guard; 43 | 44 | // Included in other source file. For improving readibility it's defined here. 45 | 46 | // ----------------------------------------------------------------------------- 47 | // If you want to print out a stacktrace everywhere SQL_WARN is called, set this 48 | // to a value > 0 - it will print out this number of stacktrace entries 49 | #define USD_SQL_DEBUG_STACKTRACE_SIZE 0 50 | 51 | #if USD_SQL_DEBUG_STACKTRACE_SIZE > 0 52 | 53 | #include 54 | 55 | #define SQL_WARN \ 56 | { \ 57 | void* backtrace_array[USD_SQL_DEBUG_STACKTRACE_SIZE]; \ 58 | size_t stack_size = \ 59 | backtrace(backtrace_array, USD_SQL_DEBUG_STACKTRACE_SIZE); \ 60 | TF_WARN("\n\n====================================\n"); \ 61 | TF_WARN("Stacktrace:\n"); \ 62 | backtrace_symbols_fd(backtrace_array, stack_size, STDERR_FILENO); \ 63 | } \ 64 | TF_WARN 65 | 66 | #else // STACKTRACE_SIZE 67 | 68 | #define SQL_WARN TF_WARN 69 | 70 | #endif // STACKTRACE_SIZE 71 | 72 | // ----------------------------------------------------------------------------- 73 | 74 | // If you want to control the number of seconds an idle connection is kept alive 75 | // for, set this to something other than zero 76 | 77 | #define SESSION_WAIT_TIMEOUT 0 78 | 79 | #if SESSION_WAIT_TIMEOUT > 0 80 | 81 | #define _USD_SQL_SIMPLE_QUOTE(ARG) #ARG 82 | #define _USD_SQL_EXPAND_AND_QUOTE(ARG) _SIMPLE_QUOTE(ARG) 83 | #define SET_SESSION_WAIT_TIMEOUT_QUERY \ 84 | ("SET SESSION wait_timeout=" _USD_SQL_EXPAND_AND_QUOTE( \ 85 | SESSION_WAIT_TIMEOUT)) 86 | #define SET_SESSION_WAIT_TIMEOUT_QUERY_STRLEN \ 87 | (sizeof(SET_SESSION_WAIT_TIMEOUT_QUERY) - 1) 88 | 89 | #endif // SESSION_WAIT_TIMEOUT 90 | 91 | // Clang tidy/static analyzer complains about this. 92 | constexpr size_t cstrlen(const char* str) { 93 | return *str != 0 ? 1 + cstrlen(str + 1) : 0; 94 | } 95 | 96 | thread_local std::once_flag thread_flag; 97 | 98 | void sql_thread_init() { 99 | std::call_once(thread_flag, []() { my_thread_init(); }); 100 | } 101 | 102 | std::string get_env_var( 103 | const std::string& server_name, const std::string& env_var, 104 | const std::string& default_value) { 105 | const auto env_first = getenv((server_name + "_" + env_var).c_str()); 106 | if (env_first != nullptr) { return env_first; } 107 | const auto env_second = getenv(env_var.c_str()); 108 | if (env_second != nullptr) { return env_second; } 109 | return default_value; 110 | } 111 | 112 | template < 113 | typename key_t, typename value_t, value_t default_value = value_t(), 114 | typename pair_t = std::pair> 115 | value_t find_in_sorted_vector( 116 | const std::vector& vec, const key_t& key) { 117 | const auto ret = std::lower_bound( 118 | vec.begin(), vec.end(), pair_t{key, default_value}, 119 | [](const pair_t& a, const pair_t& b) { return a.first < b.first; }); 120 | if (ret != vec.end() && ret->first == key) { 121 | return ret->second; 122 | } else { 123 | return default_value; 124 | } 125 | } 126 | 127 | std::string parse_path(const std::string& path) { 128 | constexpr auto schema_length_short = cstrlen(SQL_PREFIX_SHORT); 129 | constexpr auto schema_length = cstrlen(SQL_PREFIX); 130 | if (path.find(SQL_PREFIX) == 0) { 131 | return path.substr(schema_length); 132 | } else { 133 | return path.substr(schema_length_short); 134 | } 135 | } 136 | 137 | std::string clean_path(const std::string& path) { 138 | return path.find(SQL_PREFIX) == 0 139 | ? std::string(path).replace(0, cstrlen(SQL_PREFIX), SQL_PREFIX_SHORT) 140 | : path; 141 | } 142 | 143 | double convert_char_to_time(const char* raw_time) { 144 | std::tm parsed_time = {}; 145 | std::istringstream ss(raw_time); 146 | ss >> std::get_time(&parsed_time, "%Y-%m-%d %H:%M:%S"); 147 | parsed_time.tm_isdst = 0; 148 | // I have to set daylight savings to 0 149 | // for the asctime function to match the actual time 150 | // even without that, the parsed times will be consistent, so 151 | // probably it won't cause any issues 152 | 153 | // The database might use higher resolution, but the posix function is 154 | // unable to parse that so we manually have to do it. 155 | double ret = mktime(&parsed_time); 156 | const auto* dot_pos = strchr(raw_time, '.'); 157 | if (dot_pos != nullptr && *(dot_pos + 1) != '\0') { 158 | char tmp[16]; 159 | tmp[0] = '0'; 160 | tmp[1] = '.'; 161 | strncpy(tmp + 2, dot_pos + 1, 14); 162 | ret += atof(tmp); 163 | } 164 | return ret; 165 | } 166 | 167 | double convert_mysql_result_to_time( 168 | MYSQL_FIELD* field, MYSQL_ROW row, size_t field_i) { 169 | auto ret = INVALID_TIME; 170 | 171 | if (!row) { 172 | SQL_WARN("[SQLResolver] row was null"); 173 | } else if (!field) { 174 | SQL_WARN( 175 | "[SQLResolver] could not find the field type to retrieve a " 176 | "timestamp"); 177 | } else if (field->type != MYSQL_TYPE_TIMESTAMP) { 178 | SQL_WARN( 179 | "[SQLResolver] Wrong type for time field. Found %i instead of 7.", 180 | field->type); 181 | } else if (row[field_i] == nullptr) { 182 | SQL_WARN("[SQLResolver] Field %lu was null", field_i); 183 | } else if (field->max_length <= 0) { 184 | SQL_WARN("[SQLResolver] Field %lu had 0 length", field_i); 185 | } else { 186 | ret = convert_char_to_time(row[field_i]); 187 | } 188 | return ret; 189 | } 190 | 191 | double get_timestamp_raw( 192 | MYSQL* connection, const std::string& table_name, 193 | const TfToken& asset_path) { 194 | constexpr size_t query_max_length = 4096; 195 | char query[query_max_length]; 196 | snprintf( 197 | query, query_max_length, 198 | "SELECT timestamp FROM %s WHERE path = '%s' LIMIT 1", 199 | table_name.c_str(), asset_path.GetText()); 200 | unsigned long query_length = cstrlen(query); 201 | TF_DEBUG(USD_URI_SQL_RESOLVER) 202 | .Msg("get_timestamp_raw: query:\n%s\n", query); 203 | const auto query_ret = mysql_real_query(connection, query, query_length); 204 | // I only have to flush when there is a successful query. 205 | MySQLResult result; 206 | if (query_ret != 0) { 207 | SQL_WARN( 208 | "[SQLResolver] Error executing query: %s\nError code: %i\nError " 209 | "string: %s", 210 | query, mysql_errno(connection), mysql_error(connection)); 211 | } else { 212 | result.reset(mysql_store_result(connection)); 213 | } 214 | if (result == nullptr) { return INVALID_TIME; } 215 | if (mysql_num_rows(result.get()) != 1) { return INVALID_TIME; } 216 | 217 | auto row = mysql_fetch_row(result.get()); 218 | assert(mysql_num_fields(result.get()) == 1); 219 | auto field = mysql_fetch_field(result.get()); 220 | const auto time = convert_mysql_result_to_time(field, row, 0); 221 | if (time == INVALID_TIME) { 222 | TF_DEBUG(USD_URI_SQL_RESOLVER) 223 | .Msg("get_timestamp_raw: failed to convert timestamp\n"); 224 | } else { 225 | TF_DEBUG(USD_URI_SQL_RESOLVER) 226 | .Msg("get_timestamp_raw: got: %f\n", time); 227 | } 228 | return time; 229 | } 230 | 231 | enum CacheState { CACHE_MISSING, CACHE_NEEDS_FETCHING, CACHE_FETCHED }; 232 | 233 | struct Cache { 234 | CacheState state; 235 | TfToken local_path; 236 | double timestamp; 237 | std::shared_ptr asset; 238 | }; 239 | 240 | } // namespace 241 | 242 | struct SQLConnection { 243 | SQLConnection(const std::string& server_name); 244 | 245 | std::mutex connection_mutex; // do we actually need this? the api should 246 | // support multithreaded queries! 247 | std::unordered_map cached_queries; 248 | std::string table_name; 249 | MYSQL* connection; 250 | 251 | bool find_asset(const std::string& asset_path); 252 | double get_timestamp(const std::string& asset_path); 253 | std::shared_ptr open_asset(const std::string& asset_path); 254 | }; 255 | 256 | SQLResolver::SQLResolver() { my_init(); } 257 | 258 | SQLResolver::~SQLResolver() { clear(); } 259 | 260 | void SQLResolver::clear() {} 261 | 262 | SQLConnection* SQLResolver::get_connection(bool create) { 263 | sql_thread_init(); 264 | SQLConnection* conn = nullptr; 265 | { 266 | const auto server_name = getenv(HOST_ENV_VAR); 267 | if (server_name == nullptr) { 268 | SQL_WARN( 269 | "[SQLResolver] Could not get host name - make sure $%s" 270 | " is defined", 271 | HOST_ENV_VAR); 272 | return conn; 273 | } 274 | mutex_scoped_lock sc(connections_mutex); 275 | conn = find_in_sorted_vector< 276 | connection_pair::first_type, connection_pair::second_type, nullptr>( 277 | connections, server_name); 278 | if (create && conn == nullptr) { // initialize new connection 279 | conn = new SQLConnection(server_name); 280 | connections.emplace_back(server_name, conn); 281 | std::sort( 282 | connections.begin(), connections.end(), 283 | [](const connection_pair& a, const connection_pair& b) -> bool { 284 | return a.first < b.first; 285 | }); 286 | } 287 | } 288 | return conn; 289 | } 290 | 291 | std::string SQLResolver::find_asset(const std::string& path) { 292 | auto conn = get_connection(true); 293 | if (conn == nullptr) { 294 | return {}; 295 | } 296 | const auto cleaned_path = clean_path(path); 297 | return conn->find_asset(cleaned_path) ? cleaned_path : ""; 298 | } 299 | 300 | bool SQLResolver::matches_schema(const std::string& path) { 301 | constexpr auto schema_length_short = cstrlen(SQL_PREFIX_SHORT); 302 | return path.compare(0, schema_length_short, SQL_PREFIX_SHORT) == 0; 303 | } 304 | 305 | double SQLResolver::get_timestamp(const std::string& path) { 306 | auto conn = get_connection(false); 307 | return conn == nullptr ? 1.0 : conn->get_timestamp(clean_path(path)); 308 | } 309 | 310 | std::shared_ptr SQLResolver::open_asset(const std::string& path) { 311 | auto conn = get_connection(false); 312 | return conn == nullptr ? nullptr : conn->open_asset(clean_path(path)); 313 | } 314 | 315 | SQLConnection::SQLConnection(const std::string& server_name) 316 | : connection(mysql_init(nullptr)) { 317 | const auto server_user = get_env_var(server_name, USER_ENV_VAR, "root"); 318 | const auto compacted_default_pass = 319 | z85::encode_with_padding(std::string("12345678")); 320 | auto server_password = 321 | get_env_var(server_name, PASSWORD_ENV_VAR, compacted_default_pass); 322 | server_password = z85::decode_with_padding(server_password); 323 | const auto server_db = get_env_var(server_name, DB_ENV_VAR, "usd"); 324 | table_name = get_env_var(server_name, TABLE_ENV_VAR, "headers"); 325 | const auto server_port = static_cast( 326 | atoi(get_env_var(server_name, PORT_ENV_VAR, "3306").c_str())); 327 | // Turn on auto-reconnect 328 | // Note that it IS still possible for the reconnect to fail, and we 329 | // don't do any explicit check for this; experimented with also adding 330 | // a check with mysql_ping in get_connection after retrieving 331 | // a cached result, but decided against this approach, as it added 332 | // a lot of extra spam if something DID go wrong (because a lot of 333 | // resolve name calls will still "work" by just using the cached 334 | // result 335 | // Also, it's good practice to add error checking after every usage 336 | // of mysql anyway (can fail in more ways than just connection being 337 | // lost), so these will catch / print error when reconnection fails 338 | // as well 339 | my_bool reconnect = 1; 340 | mysql_options(connection, MYSQL_OPT_RECONNECT, &reconnect); 341 | const auto ret = mysql_real_connect( 342 | connection, server_name.c_str(), server_user.c_str(), 343 | server_password.c_str(), server_db.c_str(), server_port, nullptr, 0); 344 | if (ret == nullptr) { 345 | mysql_close(connection); 346 | SQL_WARN( 347 | "[SQLResolver] Failed to connect to: %s\nReason: %s", 348 | server_name.c_str(), mysql_error(connection)); 349 | connection = nullptr; 350 | } 351 | #if SESSION_WAIT_TIMEOUT > 0 352 | else { 353 | const auto query_ret = mysql_real_query( 354 | connection, SET_SESSION_WAIT_TIMEOUT_QUERY, 355 | SET_SESSION_WAIT_TIMEOUT_QUERY_STRLEN); 356 | if (query_ret != 0) { 357 | SQL_WARN( 358 | "[SQLResolver] Error executing query: %s\nError code: " 359 | "%i\nError string: %s", 360 | SET_SESSION_WAIT_TIMEOUT_QUERY, mysql_errno(connection), 361 | mysql_error(connection)); 362 | } 363 | } 364 | #endif // SESSION_WAIT_TIMEOUT 365 | } 366 | 367 | bool SQLConnection::find_asset(const std::string& asset_path) { 368 | TF_DEBUG(USD_URI_SQL_RESOLVER) 369 | .Msg("SQLConnection::find_asset: '%s'\n", asset_path.c_str()); 370 | if (connection == nullptr) { 371 | TF_DEBUG(USD_URI_SQL_RESOLVER) 372 | .Msg( 373 | "SQLConnection::find_asset: aborting due to null " 374 | "connection pointer\n"); 375 | return false; 376 | } 377 | 378 | const auto last_dot = asset_path.find_last_of('.'); 379 | if (last_dot == std::string::npos) { 380 | TF_DEBUG(USD_URI_SQL_RESOLVER) 381 | .Msg( 382 | "SQLConnection::find_asset: asset path missing extension " 383 | "('%s')\n", 384 | asset_path.c_str()); 385 | return false; 386 | } 387 | mutex_scoped_lock sc(connection_mutex); 388 | 389 | const TfToken asset_path_token(asset_path); 390 | auto cached_result = cached_queries.find(asset_path_token); 391 | 392 | auto fill_cache_data = [&](Cache& cache) -> bool { 393 | const TfToken parsed_path(parse_path(asset_path)); 394 | constexpr size_t query_max_length = 4096; 395 | char query[query_max_length]; 396 | snprintf( 397 | query, query_max_length, 398 | "SELECT EXISTS(SELECT 1 FROM %s WHERE path = '%s')", 399 | table_name.c_str(), parsed_path.GetText()); 400 | auto query_length = strlen(query); 401 | TF_DEBUG(USD_URI_SQL_RESOLVER) 402 | .Msg("SQLConnection::find_asset: query:\n%s\n", query); 403 | const auto query_ret = 404 | mysql_real_query(connection, query, query_length); 405 | // I only have to flush when there is a successful query. 406 | MySQLResult result; 407 | if (query_ret != 0) { 408 | SQL_WARN( 409 | "[SQLResolver] Error executing query: %s\nError code: " 410 | "%i\nError string: %s", 411 | query, mysql_errno(connection), mysql_error(connection)); 412 | return false; 413 | } else { 414 | result.reset(mysql_store_result(connection)); 415 | } 416 | 417 | if (result == nullptr) { return false; } 418 | 419 | assert(mysql_num_rows(result.get()) == 1); 420 | auto row = mysql_fetch_row(result.get()); 421 | assert(mysql_num_fields(result.get()) == 1); 422 | 423 | if (row[0] == nullptr || strcmp(row[0], "1") != 0) { return false; } 424 | TF_DEBUG(USD_URI_SQL_RESOLVER) 425 | .Msg("SQLConnection::find_asset: found: %s\n", asset_path.c_str()); 426 | cache.local_path = parsed_path; 427 | cache.state = CACHE_NEEDS_FETCHING; 428 | cache.timestamp = 1.0; 429 | TF_DEBUG(USD_URI_SQL_RESOLVER) 430 | .Msg( 431 | "SQLConnection::find_asset: local path: %s\n", 432 | cache.local_path.GetText()); 433 | return true; 434 | }; 435 | 436 | if (cached_result != cached_queries.end()) { 437 | if (cached_result->second.state != CACHE_MISSING) { 438 | TF_DEBUG(USD_URI_SQL_RESOLVER) 439 | .Msg( 440 | "SQLConnection::find_asset: using cached result: " 441 | "'%s'\n", 442 | cached_result->second.local_path.GetText()); 443 | return !cached_result->second.local_path.IsEmpty(); 444 | } 445 | return fill_cache_data(cached_result->second); 446 | } else { 447 | Cache cache{CACHE_MISSING, TfToken()}; 448 | const auto result = fill_cache_data(cache); 449 | cached_queries.emplace(asset_path_token, cache); 450 | return result; 451 | } 452 | } 453 | 454 | double SQLConnection::get_timestamp(const std::string& asset_path) { 455 | if (connection == nullptr) { return 1.0; } 456 | 457 | mutex_scoped_lock sc(connection_mutex); 458 | const auto cached_result = cached_queries.find(TfToken(asset_path)); 459 | if (cached_result == cached_queries.end() || 460 | cached_result->second.state == CACHE_MISSING) { 461 | SQL_WARN( 462 | "[SQLResolver] %s is missing when querying timestamps!", 463 | asset_path.c_str()); 464 | return 1.0; 465 | } 466 | const auto stamp = get_timestamp_raw( 467 | connection, table_name, cached_result->second.local_path); 468 | if (stamp == INVALID_TIME) { 469 | cached_result->second.state = CACHE_MISSING; 470 | SQL_WARN( 471 | "[SQLResolver] Failed to parse timestamp for %s, returning the" 472 | "existing value.", 473 | asset_path.c_str()); 474 | return cached_result->second.timestamp; 475 | } else if (stamp > cached_result->second.timestamp) { 476 | TF_DEBUG(USD_URI_SQL_RESOLVER) 477 | .Msg( 478 | "SQLConnection::get_timestamp: %s timestamp has changed from " 479 | "%f to %f\n", 480 | asset_path.c_str(), cached_result->second.timestamp, stamp); 481 | cached_result->second.state = CACHE_NEEDS_FETCHING; 482 | } 483 | TF_DEBUG(USD_URI_SQL_RESOLVER) 484 | .Msg( 485 | "SQLConnection::get_timestamp: timestamp of %f for %s", stamp, 486 | asset_path.c_str()); 487 | return stamp; 488 | } 489 | 490 | std::shared_ptr SQLConnection::open_asset( 491 | const std::string& asset_path) { 492 | TF_DEBUG(USD_URI_SQL_RESOLVER) 493 | .Msg("SQLConnection::open_asset: '%s'\n", asset_path.c_str()); 494 | if (connection == nullptr) { 495 | TF_DEBUG(USD_URI_SQL_RESOLVER) 496 | .Msg( 497 | "SQLConnection::open_asset: aborting due to null connection " 498 | "pointer\n"); 499 | return nullptr; 500 | } 501 | 502 | mutex_scoped_lock sc(connection_mutex); 503 | const auto cached_result = cached_queries.find(TfToken(asset_path)); 504 | if (cached_result == cached_queries.end()) { 505 | SQL_WARN( 506 | "[SQLResolver] %s was not resolved before fetching!", 507 | asset_path.c_str()); 508 | return nullptr; 509 | } 510 | 511 | if (cached_result->second.state == CACHE_MISSING) { 512 | TF_DEBUG(USD_URI_SQL_RESOLVER) 513 | .Msg( 514 | "SQLConnection::open_asset: missing from database, no fetch\n"); 515 | return nullptr; 516 | } 517 | 518 | if (cached_result->second.state == CACHE_FETCHED) { 519 | // Ensure cached state is up to date before deciding not to fetch 520 | // (there is no guarantee that get_timestamp was called prior to 521 | // fetch) 522 | auto current_timestamp = get_timestamp_raw( 523 | connection, table_name, cached_result->second.local_path); 524 | // So we can fail faster next time. 525 | if (current_timestamp == INVALID_TIME || 526 | current_timestamp <= cached_result->second.timestamp) { 527 | return cached_result->second.asset; 528 | } else { 529 | TF_DEBUG(USD_URI_SQL_RESOLVER) 530 | .Msg( 531 | "SQLConnection::open_asset: local path data is out of " 532 | "date.\n"); 533 | } 534 | } 535 | 536 | TF_DEBUG(USD_URI_SQL_RESOLVER) 537 | .Msg("SQLConnection::fetch: Cache needed fetching\n"); 538 | cached_result->second.state = 539 | CACHE_MISSING; // we'll set this up if fetching is successful 540 | 541 | MySQLResult result; 542 | constexpr size_t query_max_length = 4096; 543 | char query[query_max_length]; 544 | snprintf( 545 | query, query_max_length, 546 | "SELECT data, timestamp FROM %s WHERE path = '%s' LIMIT 1", 547 | table_name.c_str(), cached_result->second.local_path.GetText()); 548 | unsigned long query_length = strlen(query); 549 | TF_DEBUG(USD_URI_SQL_RESOLVER) 550 | .Msg("SQLConnection::open_asset: query:\n%s\n", query); 551 | const auto query_ret = mysql_real_query(connection, query, query_length); 552 | // I only have to flush when there is a successful query. 553 | if (query_ret != 0) { 554 | SQL_WARN( 555 | "[SQLResolver] Error executing query: %s\nError code: " 556 | "%i\nError string: %s", 557 | query, mysql_errno(connection), mysql_error(connection)); 558 | } else { 559 | result.reset(mysql_store_result(connection)); 560 | } 561 | 562 | if (result == nullptr) { return nullptr; } 563 | 564 | if (mysql_num_rows(result.get()) != 1) { return nullptr; } 565 | 566 | auto row = mysql_fetch_row(result.get()); 567 | assert(mysql_num_fields(result.get()) == 2); 568 | auto field = mysql_fetch_field(result.get()); 569 | if (row[0] == nullptr && field->max_length == 0) { return nullptr; } 570 | 571 | TF_DEBUG(USD_URI_SQL_RESOLVER) 572 | .Msg( 573 | "SQLConnection::open_asset: successfully fetched " 574 | "data\n"); 575 | cached_result->second.asset.reset( 576 | new MemoryAsset(row[0], field->max_length)); 577 | cached_result->second.state = CACHE_FETCHED; 578 | 579 | field = mysql_fetch_field(result.get()); 580 | cached_result->second.timestamp = 581 | convert_mysql_result_to_time(field, row, 1); 582 | if (cached_result->second.timestamp == INVALID_TIME) { 583 | TF_DEBUG(USD_URI_SQL_RESOLVER) 584 | .Msg( 585 | "SQLConnection::open_asset: failed parsing " 586 | "timestamp\n"); 587 | } 588 | return cached_result->second.asset; 589 | } 590 | 591 | PXR_NAMESPACE_CLOSE_SCOPE 592 | -------------------------------------------------------------------------------- /cmake/FindMySQL.cmake: -------------------------------------------------------------------------------- 1 | # -*- indent-tabs-mode:nil; -*- 2 | # vim: set expandtab: 3 | # 4 | # Copyright (c) 2011, 2022, Oracle and/or its affiliates. All rights reserved. 5 | # 6 | # This program is free software; you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License, version 2.0, as 8 | # published by the Free Software Foundation. 9 | # 10 | # This program is also distributed with certain software (including 11 | # but not limited to OpenSSL) that is licensed under separate terms, 12 | # as designated in a particular file or component or in included license 13 | # documentation. The authors of MySQL hereby grant you an 14 | # additional permission to link the program and your derivative works 15 | # with the separately licensed software that they have included with 16 | # MySQL. 17 | # 18 | # Without limiting anything contained in the foregoing, this file, 19 | # which is part of MySQL Connector/ODBC, is also subject to the 20 | # Universal FOSS Exception, version 1.0, a copy of which can be found at 21 | # http://oss.oracle.com/licenses/universal-foss-exception. 22 | # 23 | # This program is distributed in the hope that it will be useful, but 24 | # WITHOUT ANY WARRANTY; without even the implied warranty of 25 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 26 | # See the GNU General Public License, version 2.0, for more details. 27 | # 28 | # You should have received a copy of the GNU General Public License 29 | # along with this program; if not, write to the Free Software Foundation, Inc., 30 | # 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 31 | 32 | ########################################################################## 33 | 34 | ########################################################################## 35 | # 36 | # Configuration variables, all optional, are 37 | # 38 | # MYSQL_DIR - Set in environment or as parameter to "cmake", 39 | # this is the top directory of the MySQL Server or 40 | # Connector/C install 41 | # MYSQL_INCLUDE_DIR - Set in environment or as parameter to "cmake", 42 | # this is the include directory where to find 43 | # the client library 44 | # MYSQL_LIB_DIR - Set in environment or as parameter to "cmake", 45 | # this is the library directory where to find 46 | # the client library 47 | # MYSQLCLIENT_STATIC_LINKING 48 | # - Specify that you want static linking, dynamic 49 | # linking is the default 50 | # MYSQLCLIENT_NO_THREADS 51 | # - Specify to link against the single threaded 52 | # library, "libmysqlclient". Note that in 5.5 53 | # and up "libmysqlclient" is multithreaded and 54 | # "libmysqlclient_r" just a soft link to it 55 | # MYSQL_CONFIG_EXECUTABLE 56 | # - "mysql_config" executable to use 57 | # MYSQL_CXX_LINKAGE - Specify that client library needs C++ linking 58 | # MYSQL_EXTRA_LIBRARIES 59 | # - Libraries to add to the linkage 60 | # MYSQL_CFLAGS - C compiler flags 61 | # MYSQL_CXXFLAGS - C++ compiler flags 62 | # MYSQL_LINK_FLAGS - User defined extra linkage flags 63 | # FINDMYSQL_DEBUG - Set if want debug output from this script 64 | # 65 | # Note that most variables above, if not set by the user they will be 66 | # set by this include file. 67 | # 68 | # In addition, the below CMake variables are set by this include file 69 | # 70 | # MYSQL_VERSION - Three position numeric version, like 5.6.41 71 | # MYSQL_VERSION_ID - Numeric padded version, 5.13.4 => 51304 72 | # MYSQL_NUM_VERSION - Same as MYSQL_VERSION_ID, for compatibility 73 | # MYSQL_LIB - Path to the client library 74 | # MYSQL_LIBRARIES - Library name, might be "-lmysqlclient" while 75 | # MYSQL_LIB is the path to the library 76 | # MYSQL_CLIENT_LIBS - Same as MYSQL_LIBRARIES, for compatibility 77 | # 78 | # (1) If MYSQL_INCLUDE_DIR or MYSQL_LIB_DIR are given, these are 79 | # used and an error is reported if can't be used 80 | # (2) If MYSQL_CONFIG_EXECUTABLE is given, it is used to get 81 | # headers and libraries 82 | # (3) If MYSQL_DIR is given and "${MYSQL_DIR}/bin/mysql_config" is 83 | # found, then same as (2) 84 | # (4) If MYSQL_DIR is given and no "${MYSQL_DIR}/bin/mysql_config", 85 | # search MYSQL_DIR 86 | # 87 | # FIXME if we get a "mysql_config" on Windows, things needs to change 88 | # FIXME rename the VERSION variables above 89 | # FIXME let MYSQL_VERSION include "-beta" etc? 90 | # FIXME can mysql_config --version be C/C verson? 91 | # FIXME if no mysql_config, find version from include/mysql_version.h? 92 | # #define MYSQL_SERVER_VERSION "5.7.5-m15" 93 | # #define MYSQL_VERSION_ID 50705 94 | # #define LIBMYSQL_VERSION "6.1.5" 95 | # #define LIBMYSQL_VERSION_ID 60105 96 | # FIXME can MYSQL_LIB_DIR be a list of paths? 97 | # FIXME is MYSQLCLIENT_LIBS a better name? 98 | # FIXME cache variables, makes them command line args? 99 | # FIXME really do include_directories() and link_directories()? Likely 100 | # FIXME add check that if not static, not picked up .a or mysqlclient.lib 101 | # FIXME MYSQL_VERSION_ID need to take into account Cluster versions 102 | # and Connector/C versions 103 | # FIXME handle MYSQL_VERSION_ID, LIBMYSQL_VERSION and LIBMYSQL_VERSION_ID? 104 | # 105 | ########################################################################## 106 | 107 | 108 | ########################################################################## 109 | # 110 | # Check the input data 111 | # 112 | ########################################################################## 113 | 114 | # If using both MYSQL_DIR as a cmake argument and set in environment, 115 | # and not empty strings, they better be the same. Else stop and complain 116 | 117 | set(ENV_OR_OPT_VARS 118 | MYSQL_DIR 119 | MYSQL_INCLUDE_DIR 120 | MYSQL_LIB_DIR 121 | MYSQL_LIB_DIR_LIST 122 | MYSQL_PLUGIN_DIR 123 | MYSQL_CFLAGS 124 | MYSQL_CXXFLAGS 125 | MYSQL_CONFIG_EXECUTABLE 126 | MYSQLCLIENT_STATIC_LINKING 127 | MYSQLCLIENT_NO_THREADS 128 | MYSQL_CXX_LINKAGE 129 | MYSQL_EXTRA_LIBRARIES 130 | MYSQL_LINK_FLAGS 131 | ) 132 | 133 | # Mark the variable names that have values that are paths 134 | set(ENV_OR_OPT_PATH_VARS 135 | MYSQL_DIR 136 | MYSQL_INCLUDE_DIR 137 | MYSQL_LIB_DIR 138 | MYSQL_PLUGIN_DIR 139 | ) 140 | 141 | 142 | foreach(_xvar ${ENV_OR_OPT_VARS}) 143 | 144 | if((DEFINED ${_xvar}) AND 145 | (DEFINED ENV{${_xvar}}) AND 146 | (NOT "${${_xvar}}" STREQUAL "") AND 147 | (NOT "$ENV{${_xvar}}" STREQUAL "") AND 148 | (NOT "$ENV{${_xvar}}" STREQUAL "${${_xvar}}")) 149 | message(FATAL_ERROR "Please pass -D${_xvar}=... as an argument or " 150 | "set ${_xvar} in the environment, but not both") 151 | endif() 152 | 153 | # Now we know both are not set, set the CMake variable if needed 154 | if((DEFINED ENV{${_xvar}}) AND (NOT "$ENV{${_xvar}}" STREQUAL "")) 155 | set(${_xvar} $ENV{${_xvar}}) 156 | endif() 157 | 158 | # Notmalize the path if the variable is set and is a path 159 | if(${_xvar}) 160 | list(FIND ENV_OR_OPT_PATH_VARS ${_xvar} _index) 161 | if (${_index} GREATER -1) 162 | file(TO_CMAKE_PATH "${${_xvar}}" ${_xvar}) 163 | get_filename_component(${_xvar} ${${_xvar}} ABSOLUTE) 164 | endif() 165 | endif() 166 | 167 | endforeach() 168 | 169 | 170 | # Bail out if both MYSQL_DIR/MYSQL_CONFIG_EXECUTABLE and MYSQL_INCLUDE/LIB_DIR 171 | # were given 172 | 173 | if(MYSQL_DIR AND (MYSQL_INCLUDE_DIR OR MYSQL_LIB_DIR OR MYSQL_PLUGIN_DIR)) 174 | message(FATAL_ERROR 175 | "Both MYSQL_DIR and MYSQL_INCLUDE_DIR/MYSQL_LIB_DIR/MYSQL_PLUGIN_DIR were specified," 176 | " use either one or the other way of pointing at MySQL location." 177 | ) 178 | endif() 179 | 180 | 181 | if (MYSQL_CONFIG_EXECUTABLE AND (MYSQL_INCLUDE_DIR OR MYSQL_LIB_DIR OR MYSQL_PLUGIN_DIR)) 182 | message(FATAL_ERROR 183 | "Both MYSQL_CONFIG_EXECUTABLE and MYSQL_INCLUDE_DIR/MYSQL_LIB_DIR/MYSQL_PLUGIN_DIR were specified," 184 | " mixing settings detected with mysql_config and manually set by variables" 185 | " is not supported and would confuse our build logic." 186 | ) 187 | endif() 188 | 189 | 190 | if(MYSQL_CONFIG_EXECUTABLE) 191 | set(_mysql_config_set_by_user 1) 192 | else() 193 | # If MYSQL_DIR is set, set MYSQL_CONFIG_EXECUTABLE 194 | if((NOT WIN32) AND 195 | (DEFINED MYSQL_DIR) AND 196 | (EXISTS "${MYSQL_DIR}/bin/mysql_config")) 197 | set(MYSQL_CONFIG_EXECUTABLE "${MYSQL_DIR}/bin/mysql_config") 198 | set(_mysql_config_in_mysql_dir 1) 199 | endif() 200 | endif() 201 | 202 | 203 | ########################################################################## 204 | # 205 | # Data and basic settings 206 | # 207 | ########################################################################## 208 | 209 | # Set sub directory to search in 210 | # dist = for mysql binary distributions 211 | # build = for custom built tree 212 | 213 | if(CMAKE_BUILD_TYPE STREQUAL Debug) 214 | set(_lib_suffix_dist debug) 215 | set(_lib_suffix_build Debug) 216 | else() 217 | set(_lib_suffix_dist opt) 218 | set(_lib_suffix_build Release) 219 | add_definitions(-DNDEBUG) # FIXME what?! 220 | endif() 221 | 222 | set(_exe_fallback_path 223 | /usr/bin 224 | /usr/local/bin 225 | /opt/mysql/mysql/bin 226 | /usr/local/mysql/bin 227 | ) 228 | 229 | set(_include_fallback_path 230 | /usr/include/mysql 231 | /usr/local/include/mysql 232 | /opt/mysql/mysql/include 233 | /opt/mysql/mysql/include/mysql 234 | /usr/local/mysql/include 235 | /usr/local/mysql/include/mysql 236 | $ENV{ProgramFiles}/MySQL/*/include 237 | $ENV{SystemDrive}/MySQL/*/include 238 | ) 239 | 240 | set(_lib_fallback_path 241 | /usr/lib/mysql 242 | /usr/local/lib/mysql 243 | /usr/local/mysql/lib 244 | /usr/local/mysql/lib/mysql 245 | /opt/mysql/mysql/lib 246 | /opt/mysql/mysql/lib/mysql 247 | $ENV{ProgramFiles}/MySQL/*/lib/${_lib_suffix_dist} 248 | $ENV{ProgramFiles}/MySQL/*/lib 249 | $ENV{SystemDrive}/MySQL/*/lib/${_lib_suffix_dist} 250 | $ENV{SystemDrive}/MySQL/*/lib 251 | ) 252 | 253 | set(_lib_subdirs 254 | # Paths in build tree, really being too nice 255 | libmysql/${_lib_suffix_build} 256 | client/${_lib_suffix_build} 257 | libmysql_r/.libs 258 | libmysql/.libs 259 | libmysql 260 | # Install sub directories 261 | lib/mysql 262 | lib/${_lib_suffix_dist} # Need to be before "lib" 263 | lib 264 | ) 265 | 266 | set(_static_subdirs 267 | mysql 268 | ${_lib_suffix_dist} 269 | ) 270 | 271 | if(MSVC90) 272 | set(_vs_subdir vs9) 273 | elseif(MSVC10) 274 | set(_vs_subdir vs10) 275 | elseif(MSVC11) 276 | set(_vs_subdir vs11) 277 | elseif(MSVC12) 278 | set(_vs_subdir vs12) 279 | elseif(MSVC13) 280 | set(_vs_subdir vs13) 281 | elseif(MSVC14) 282 | set(_vs_subdir vs14) 283 | elseif(MSVC15) 284 | set(_vs_subdir vs15) 285 | endif() 286 | 287 | if(_vs_subdir) 288 | if("${_lib_suffix_dist}" STREQUAL "debug") 289 | set(_vs_subdir "${_vs_subdir}/debug") 290 | endif() 291 | list(INSERT _lib_subdirs 0 "lib/${_vs_subdir}") 292 | endif() 293 | 294 | # For Windows, the client library name differs, so easy to 295 | # make sure find_library() picks the right one. For Unix, it 296 | # is the file extension that differs. In the static library 297 | # case we know it is ".a", so we add it to the library name 298 | # we search for to make sure it is picked in the static case. 299 | if(WIN32) 300 | set(_dynamic_libs "libmysql") 301 | set(_static_libs "mysqlclient") 302 | set(_static_lib_ext ".lib") # Careful, can be import library for DLL 303 | elseif(MYSQLCLIENT_NO_THREADS) 304 | # In 5.1 and below there is a single threaded library 305 | set(_dynamic_libs "mysqlclient") 306 | set(_static_libs "libmysqlclient.a") 307 | set(_static_lib_ext ".a") 308 | else() 309 | # We try the multithreaded "libmysqlclient_r" first and if not 310 | # there, pick "libmysqlclient" that in 5.5 and up is multithreaded 311 | # anyway (soft link "libmysqlclient_r" is not installed MySQL Server 312 | # 5.6 and Debian/Ubuntu and might go in 5.7 for all installs) 313 | set(_dynamic_libs "mysqlclient_r" "mysqlclient") 314 | set(_static_libs "libmysqlclient_r.a" "libmysqlclient.a") 315 | set(_static_lib_ext ".a") 316 | endif() 317 | 318 | if(MYSQLCLIENT_STATIC_LINKING) 319 | set(_link_type "static") 320 | set(_search_libs ${_static_libs}) 321 | else() 322 | set(_link_type "dynamic") 323 | set(_search_libs ${_dynamic_libs}) 324 | endif() 325 | 326 | # Just to pretty print in error messages 327 | string(REPLACE ";" " " _pp_search_libs "${_search_libs}") 328 | string(REPLACE ";" " " _pp_lib_subdirs "${_lib_subdirs}") 329 | string(REPLACE ";" " " _pp_lib_fallback_path "${_lib_fallback_path}") 330 | string(REPLACE ";" " " _pp_include_fallback_path "${_include_fallback_path}") 331 | 332 | message(STATUS "You will link ${_link_type}ally to the MySQL client" 333 | " library (set with -DMYSQLCLIENT_STATIC_LINKING=)") 334 | message(STATUS "Searching for ${_link_type} libraries with the base name(s) \"${_pp_search_libs}\"") 335 | 336 | ########################################################################## 337 | # 338 | # Macros 339 | # 340 | ########################################################################## 341 | 342 | # ---------------------------------------------------------------------- 343 | # 344 | # Macro that runs "mysql_config ${_opt}" and return the line after 345 | # trimming away ending space/newline. 346 | # 347 | # _mysql_conf( 348 | # _var - output variable name, will contain a ';' separated list 349 | # _opt - the flag to give to mysql_config 350 | # 351 | # ---------------------------------------------------------------------- 352 | 353 | macro(_mysql_conf _var _opt) 354 | execute_process( 355 | COMMAND ${MYSQL_CONFIG_EXECUTABLE} ${_opt} 356 | OUTPUT_VARIABLE ${_var} 357 | OUTPUT_STRIP_TRAILING_WHITESPACE 358 | ) 359 | endmacro() 360 | 361 | # ---------------------------------------------------------------------- 362 | # 363 | # Macro that runs "mysql_config ${_opt}", selects output args using a 364 | # regex, and clean it up a bit removing space/tab/newline before 365 | # setting it to a variable. 366 | # 367 | # _mysql_config( 368 | # _var - output variable name, will contain a ';' separated list 369 | # _regex - regular expression matching the prefix of args to select 370 | # _opt - the flag to give to mysql_config 371 | # 372 | # ---------------------------------------------------------------------- 373 | 374 | macro(_mysql_config _var _regex _opt) 375 | _mysql_conf(_mysql_config_output ${_opt}) 376 | string(REGEX MATCHALL "${_regex}([^ ]+)" _mysql_config_output "${_mysql_config_output}") 377 | string(REGEX REPLACE "^[ \t]+" "" _mysql_config_output "${_mysql_config_output}") 378 | IF(CMAKE_SYSTEM_NAME MATCHES "SunOS") 379 | string(REGEX REPLACE " -latomic" "" _mysql_config_output "${_mysql_config_output}") 380 | ENDIF() 381 | string(REGEX REPLACE "${_regex}" "" _mysql_config_output "${_mysql_config_output}") 382 | separate_arguments(_mysql_config_output) 383 | set(${_var} ${_mysql_config_output}) 384 | endmacro() 385 | 386 | # ---------------------------------------------------------------------- 387 | # 388 | # Macro that runs "mysql_config ${_opt}" and selects output using a 389 | # prefix regex. Cleans it up a bit removing space/tab/newline. Then 390 | # removes the prefix on all in the list, and finally replace what 391 | # matches another regular expression with a replacement string. 392 | # 393 | # _mysql_config_replace( 394 | # _var - output variable name, will contain a ';' separated list 395 | # _regex1 - regular expression to match out arguments 396 | # _replace - what to replace match _regex1 with 397 | # _regex2 - regular expression matching the prefix of args to select 398 | # _opt - the flag to give to mysql_config 399 | # 400 | # ---------------------------------------------------------------------- 401 | 402 | macro(_mysql_config_replace _var _regex1 _replace _regex2 _opt) 403 | _mysql_conf(_mysql_config_output ${_opt}) 404 | string(REGEX MATCHALL "${_regex2}([^ ]+)" _mysql_config_output "${_mysql_config_output}") 405 | string(REGEX REPLACE "^[ \t]+" "" _mysql_config_output "${_mysql_config_output}") 406 | IF(CMAKE_SYSTEM_NAME MATCHES "SunOS") 407 | string(REGEX REPLACE " -latomic" "" _mysql_config_output "${_mysql_config_output}") 408 | ENDIF() 409 | string(REGEX REPLACE "${_regex2}" "" _mysql_config_output "${_mysql_config_output}") 410 | string(REGEX REPLACE "${_regex1}" "${_replace}" _mysql_config_output "${_mysql_config_output}") 411 | separate_arguments(_mysql_config_output) 412 | set(${_var} ${_mysql_config_output}) 413 | endmacro() 414 | 415 | 416 | # ---------------------------------------------------------------------- 417 | # 418 | # Macro to check that we found a library and that we got the right type 419 | # 420 | # ---------------------------------------------------------------------- 421 | 422 | macro(_check_lib_search_error _lib_dir_var _lib_var _exta_err_string) 423 | 424 | set(_lib "${${_lib_var}}") 425 | set(_lib_dir "${${_lib_dir_var}}") 426 | 427 | if(FINDMYSQL_DEBUG) 428 | message("_lib \"${_lib}\"") 429 | message("_lib_dir \"${_lib_dir}\"") 430 | message("_lib_var \"${_lib_var}\"") 431 | message("_lib_dir_var \"${_lib_dir_var}\"") 432 | endif() 433 | 434 | set(_err_string "Could not find ${_link_type} " 435 | "\"${_pp_search_libs}\" in ${_lib_dir_var} " 436 | "\"${_lib_dir}\" ${_exta_err_string}") 437 | 438 | if(NOT ${_lib_var}) 439 | message(FATAL_ERROR ${_err_string}) 440 | endif() 441 | 442 | # find_library() try find a shared library first, then a static 443 | # one. For Windows the library has a different name, but for 444 | # Unix only the extension differs. So we check here that we 445 | # got the library kind we expected. 446 | if(NOT WIN32) 447 | if(NOT MYSQLCLIENT_STATIC_LINKING) 448 | get_filename_component(_ext ${_lib} EXT) 449 | if(${_ext} STREQUAL ${_static_lib_ext}) 450 | message(FATAL_ERROR ${_err_string}) 451 | endif() 452 | endif() 453 | endif() 454 | endmacro() 455 | 456 | 457 | ########################################################################## 458 | # 459 | # Try find MYSQL_CONFIG_EXECUTABLE if not set, and find version 460 | # 461 | ########################################################################## 462 | 463 | if(NOT WIN32) 464 | 465 | if(NOT MYSQL_CONFIG_EXECUTABLE) 466 | find_program(MYSQL_CONFIG_EXECUTABLE 467 | NAMES 468 | mysql_config 469 | DOC 470 | "full path of mysql_config" 471 | PATHS 472 | ${_exe_fallback_path} 473 | ) 474 | endif() 475 | 476 | if(MYSQL_CONFIG_EXECUTABLE) 477 | message(STATUS "mysql_config was found ${MYSQL_CONFIG_EXECUTABLE}") 478 | 479 | _mysql_conf(MYSQL_VERSION "--version") 480 | endif() 481 | 482 | endif() 483 | 484 | ########################################################################## 485 | # 486 | # Find MYSQL_INCLUDE_DIR 487 | # 488 | ########################################################################## 489 | 490 | if(FINDMYSQL_DEBUG AND MYSQL_INCLUDE_DIR) 491 | message("DBG: User gave MYSQL_INCLUDE_DIR = \"${MYSQL_INCLUDE_DIR}\"") 492 | endif() 493 | 494 | if(FINDMYSQL_DEBUG AND MYSQL_DIR) 495 | message("DBG: User gave MYSQL_DIR = \"${MYSQL_DIR}\"") 496 | endif() 497 | 498 | if(MYSQL_INCLUDE_DIR) 499 | 500 | if(FINDMYSQL_DEBUG) 501 | message("DBG: Using MYSQL_INCLUDE_DIR to find \"mysql.h\"") 502 | endif() 503 | 504 | if(NOT EXISTS "${MYSQL_INCLUDE_DIR}/mysql.h") 505 | message(FATAL_ERROR "MYSQL_INCLUDE_DIR given, but no \"mysql.h\" " 506 | "in \"${MYSQL_INCLUDE_DIR}\"") 507 | endif() 508 | 509 | elseif(MYSQL_DIR AND 510 | (NOT _mysql_config_in_mysql_dir) AND 511 | (NOT _mysql_config_set_by_user)) 512 | 513 | if(FINDMYSQL_DEBUG) 514 | message("DBG: Using MYSQL_DIR without \"mysql_config\" to find \"mysql.h\"") 515 | endif() 516 | 517 | set(MYSQL_INCLUDE_DIR "${MYSQL_DIR}/include") 518 | if(NOT EXISTS "${MYSQL_INCLUDE_DIR}/mysql.h") 519 | message(FATAL_ERROR "MYSQL_DIR given, but no \"mysql.h\" " 520 | "in \"${MYSQL_INCLUDE_DIR}\"") 521 | endif() 522 | 523 | elseif(MYSQL_CONFIG_EXECUTABLE) 524 | 525 | if(FINDMYSQL_DEBUG) 526 | message("DBG: Using \"mysql_config\" to find \"mysql.h\"") 527 | endif() 528 | 529 | # This code assumes there is just one "-I...." and that 530 | # no space between "-I" and the path 531 | _mysql_config(MYSQL_INCLUDE_DIR "(^| )-I" "--include") 532 | if(NOT MYSQL_INCLUDE_DIR) 533 | message(FATAL_ERROR "Could not find the include dir from running " 534 | "\"${MYSQL_CONFIG_EXECUTABLE}\"") 535 | endif() 536 | 537 | if(NOT EXISTS "${MYSQL_INCLUDE_DIR}/mysql.h") 538 | message(FATAL_ERROR "Could not find \"mysql.h\" in \"${MYSQL_INCLUDE_DIR}\" " 539 | "found from running \"${MYSQL_CONFIG_EXECUTABLE}\"") 540 | endif() 541 | 542 | else() 543 | 544 | if(FINDMYSQL_DEBUG) 545 | message("DBG: Using find_path() searching " 546 | "\"${_pp_include_fallback_path}\" to find \"mysql.h\"") 547 | endif() 548 | 549 | # No specific paths, try some common install paths 550 | find_path(MYSQL_INCLUDE_DIR mysql.h ${_include_fallback_path}) 551 | 552 | if(NOT MYSQL_INCLUDE_DIR) 553 | message(FATAL_ERROR "Could not find \"mysql.h\" from searching " 554 | "\"${_pp_include_fallback_path}\"") 555 | endif() 556 | 557 | endif() 558 | 559 | if(FINDMYSQL_DEBUG) 560 | message("DBG: MYSQL_INCLUDE_DIR = \"${MYSQL_INCLUDE_DIR}\"") 561 | endif() 562 | 563 | ########################################################################## 564 | # 565 | # Find MYSQL_LIB_DIR, MYSQL_LIB, MYSQL_PLUGIN_DIR and MYSQL_LIBRARIES 566 | # 567 | ########################################################################## 568 | 569 | if(FINDMYSQL_DEBUG AND MYSQL_LIB_DIR) 570 | message("DBG: User gave MYSQL_LIB_DIR = \"${MYSQL_LIB_DIR}\"") 571 | endif() 572 | 573 | if(MYSQL_LIB_DIR) 574 | 575 | if(FINDMYSQL_DEBUG) 576 | message("DBG: Using find_library() searching MYSQL_LIB_DIR") 577 | endif() 578 | 579 | find_library(MYSQL_LIB 580 | NAMES 581 | ${_search_libs} 582 | PATHS 583 | "${MYSQL_LIB_DIR}" 584 | NO_DEFAULT_PATH 585 | ) 586 | _check_lib_search_error(MYSQL_LIB_DIR MYSQL_LIB "") 587 | set(MYSQL_LIBRARIES ${MYSQL_LIB}) 588 | 589 | if(NOT DEFINED MYSQL_PLUGIN_DIR) 590 | set(MYSQL_PLUGIN_DIR "${MYSQL_LIB_DIR}/plugin") 591 | endif() 592 | 593 | elseif(MYSQL_DIR AND 594 | (NOT _mysql_config_in_mysql_dir) AND 595 | (NOT _mysql_config_set_by_user)) 596 | 597 | if(FINDMYSQL_DEBUG) 598 | message("DBG: Using find_library() searching " 599 | "MYSQL_DIR and \"${_pp_lib_subdirs}\"") 600 | endif() 601 | 602 | find_library(MYSQL_LIB 603 | NAMES 604 | ${_search_libs} 605 | PATHS 606 | "${MYSQL_DIR}" 607 | PATH_SUFFIXES 608 | ${_lib_subdirs} 609 | NO_DEFAULT_PATH 610 | ) 611 | _check_lib_search_error(MYSQL_DIR MYSQL_LIB "in \"${_pp_lib_subdirs}\"") 612 | get_filename_component(MYSQL_LIB_DIR "${MYSQL_LIB}" PATH) 613 | set(MYSQL_LIBRARIES "${MYSQL_LIB}") 614 | 615 | if(((NOT DEFINED MYSQL_PLUGIN_DIR) OR (NOT ${MYSQL_PLUGIN_DIR})) AND MYSQL_LIB_DIR) 616 | if(EXISTS "${MYSQL_LIB_DIR}/plugin") 617 | set(MYSQL_PLUGIN_DIR "${MYSQL_LIB_DIR}/plugin") 618 | else() 619 | #If directory does not exist it must be a debug dir layout 620 | if(EXISTS "${MYSQL_LIB_DIR}/../plugin/") 621 | set(MYSQL_PLUGIN_DIR "${MYSQL_LIB_DIR}/../plugin") 622 | endif() 623 | endif() 624 | endif() 625 | 626 | elseif(MYSQL_CONFIG_EXECUTABLE) 627 | 628 | if(FINDMYSQL_DEBUG) 629 | message("DBG: Using \"mysql_config\" to find the libraries") 630 | endif() 631 | 632 | # This code assumes there is just one "-L...." and that 633 | # no space between "-L" and the path 634 | _mysql_config(MYSQL_LIB_DIR "(^| )-L" "--libs") 635 | _mysql_conf(MYSQL_PLUGIN_DIR "--variable=plugindir") 636 | 637 | IF(CMAKE_SYSTEM_NAME MATCHES "SunOS") 638 | # This is needed to make Solaris binaries using the default runtime lib path 639 | _mysql_config(DEV_STUDIO_RUNTIME_DIR "(^| )-R" "--libs") 640 | ENDIF() 641 | 642 | 643 | LIST(LENGTH MYSQL_LIB_DIR dir_cnt) 644 | MESSAGE(STATUS "Libraries paths found: ${n}") 645 | IF(${dir_cnt} GREATER 1) 646 | SET(MYSQL_LIB_DIR_LIST ${MYSQL_LIB_DIR}) 647 | MESSAGE(STATUS "MYSQL_LIB_DIR_LIST = ${MYSQL_LIB_DIR_LIST}") 648 | 649 | FOREACH(_path_to_check IN LISTS MYSQL_LIB_DIR) 650 | FIND_LIBRARY(_mysql_client_lib_var 651 | NAMES ${_search_libs} 652 | PATHS ${_path_to_check} 653 | NO_DEFAULT_PATH 654 | ) 655 | IF(_mysql_client_lib_var) 656 | MESSAGE(STATUS "CLIENT LIB VAR: ${_mysql_client_lib_var}") 657 | unset(_mysql_client_lib_var CACHE) 658 | set(MYSQL_LIB_DIR ${_path_to_check}) 659 | ENDIF() 660 | ENDFOREACH(_path_to_check) 661 | ENDIF() 662 | 663 | if(NOT MYSQL_LIB_DIR) 664 | message(FATAL_ERROR "Could not find the library dir from running " 665 | "\"${MYSQL_CONFIG_EXECUTABLE}\"") 666 | endif() 667 | 668 | if(NOT EXISTS "${MYSQL_LIB_DIR}") 669 | message(FATAL_ERROR "Could not find the directory \"${MYSQL_LIB_DIR}\" " 670 | "found from running \"${MYSQL_CONFIG_EXECUTABLE}\"") 671 | endif() 672 | 673 | # We have the assumed MYSQL_LIB_DIR. The output from "mysql_config" 674 | # might not be correct for static libraries, so we might need to 675 | # adjust MYSQL_LIB_DIR later on. 676 | 677 | if(MYSQLCLIENT_STATIC_LINKING) 678 | 679 | # Find the static library, might be one level down 680 | find_library(MYSQL_LIB 681 | NAMES 682 | ${_search_libs} 683 | PATHS 684 | ${MYSQL_LIB_DIR} 685 | PATH_SUFFIXES 686 | ${_static_subdirs} 687 | NO_DEFAULT_PATH 688 | ) 689 | _check_lib_search_error(MYSQL_LIB_DIR MYSQL_LIB "in \"${_static_subdirs}\"") 690 | 691 | # Adjust MYSQL_LIB_DIR in case it changes 692 | get_filename_component(MYSQL_LIB_DIR "${MYSQL_LIB}" PATH) 693 | 694 | # Replace the current library references with the full path 695 | # to the library, i.e. the -L will be ignored 696 | _mysql_config_replace(MYSQL_LIBRARIES 697 | "(mysqlclient|mysqlclient_r)" "${MYSQL_LIB}" "(^| )-l" "--libs") 698 | 699 | else() 700 | 701 | _mysql_config(MYSQL_LIBRARIES "(^| )-l" "--libs") 702 | FOREACH(__lib IN LISTS MYSQL_LIBRARIES) 703 | string(REGEX MATCH "mysqlclient([^ ]*)" _matched_lib __lib) 704 | IF(_matched_lib) 705 | set(_search_libs ${matched_lib}) 706 | ENDIF() 707 | ENDFOREACH() 708 | # First library is assumed to be the client library 709 | # list(GET MYSQL_LIBRARIES 0 _search_libs) 710 | find_library(MYSQL_LIB 711 | NAMES 712 | ${_search_libs} 713 | PATHS 714 | ${MYSQL_LIB_DIR} 715 | NO_DEFAULT_PATH 716 | ) 717 | _check_lib_search_error(MYSQL_LIB_DIR MYSQL_LIB "") 718 | 719 | endif() 720 | 721 | else() 722 | 723 | if(FINDMYSQL_DEBUG) 724 | message("DBG: Using find_library() searching " 725 | "\"${_pp_lib_fallback_path}\" to find the client library") 726 | endif() 727 | 728 | # Search standard places 729 | find_library(MYSQL_LIB 730 | NAMES 731 | ${_search_libs} 732 | PATHS 733 | ${_lib_fallback_path} 734 | ) 735 | if(NOT MYSQL_LIB) 736 | message(FATAL_ERROR "Could not find \"${_pp_search_libs}\" from searching " 737 | "\"${_pp_lib_fallback_path}\"") 738 | endif() 739 | 740 | get_filename_component(MYSQL_LIB_DIR "${MYSQL_LIB}" PATH) 741 | 742 | endif() 743 | 744 | ########################################################################## 745 | # 746 | # Add more libraries to MYSQL_LIBRARIES 747 | # 748 | ########################################################################## 749 | 750 | # FIXME needed?! 751 | if(MYSQLCLIENT_STATIC_LINKING AND 752 | NOT WIN32 AND 753 | NOT ${CMAKE_SYSTEM_NAME} MATCHES "Darwin") 754 | list(APPEND MYSQL_LIBRARIES "rt") 755 | endif() 756 | 757 | # For dynamic linking use the built-in sys and strings 758 | if(NOT MYSQLCLIENT_STATIC_LINKING) 759 | list(APPEND SYS_LIBRARIES "mysql_sys") 760 | list(APPEND SYS_LIBRARIES "mysql_strings") 761 | list(APPEND SYS_LIBRARIES ${MYSQL_LIBRARIES}) 762 | SET(MYSQL_LIBRARIES ${SYS_LIBRARIES}) 763 | 764 | #if(NOT MYSQLCLIENT_STATIC_LINKING AND ${CMAKE_SYSTEM_NAME} MATCHES "Darwin") 765 | # list(REVERSE MYSQL_LIBRARIES) 766 | #endif() 767 | 768 | endif() 769 | 770 | if(MYSQL_EXTRA_LIBRARIES) 771 | separate_arguments(MYSQL_EXTRA_LIBRARIES) 772 | list(APPEND MYSQL_LIBRARIES ${MYSQL_EXTRA_LIBRARIES}) 773 | endif() 774 | 775 | list(LENGTH MYSQL_LIBRARIES len) 776 | if (MYSQL_STATIC_LINKING AND (len LESS 2)) 777 | message(WARNING 778 | "Statically linking MySQL client library normally requires linking" 779 | " additional libraries that the client library depends on. It seems" 780 | " no extra libraries have been specified. Provide the list of required" 781 | " dependencies through MYSQL_EXTRA_LIBRARIES." 782 | ) 783 | endif() 784 | 785 | # For compatibility 786 | SET(MYSQL_CLIENT_LIBS ${MYSQL_LIBRARIES}) 787 | 788 | ########################################################################## 789 | # 790 | # If not found MySQL Serverv version, compile a small client app 791 | # and let it write a small cmake file with the settings 792 | # 793 | ########################################################################## 794 | 795 | if(MYSQL_INCLUDE_DIR AND NOT MYSQL_VERSION) 796 | 797 | # Write the C source file that will include the MySQL headers 798 | set(GETMYSQLVERSION_SOURCEFILE "${CMAKE_CURRENT_BINARY_DIR}/getmysqlversion.c") 799 | file(WRITE "${GETMYSQLVERSION_SOURCEFILE}" 800 | "#include \n" 801 | "#include \n" 802 | "int main() {\n" 803 | " printf(\"%s\", MYSQL_SERVER_VERSION);\n" 804 | "}\n" 805 | ) 806 | 807 | # Compile and run the created executable, store output in MYSQL_VERSION 808 | try_run(_run_result _compile_result 809 | "${CMAKE_BINARY_DIR}" 810 | "${GETMYSQLVERSION_SOURCEFILE}" 811 | CMAKE_FLAGS "-DINCLUDE_DIRECTORIES:STRING=${MYSQL_INCLUDE_DIR}" 812 | RUN_OUTPUT_VARIABLE MYSQL_VERSION 813 | ) 814 | 815 | if(FINDMYSQL_DEBUG) 816 | if(NOT _compile_result) 817 | message("DBG: Could not compile \"getmysqlversion.c\"") 818 | endif() 819 | if(_run_result) 820 | message("DBG: Running \"getmysqlversion\" returned ${_run_result}") 821 | endif() 822 | endif() 823 | 824 | endif() 825 | 826 | ########################################################################## 827 | # 828 | # Clean up MYSQL_VERSION and create MYSQL_VERSION_ID/MYSQL_NUM_VERSION 829 | # 830 | ########################################################################## 831 | 832 | if(NOT MYSQL_VERSION) 833 | message(FATAL_ERROR "Could not determine the MySQL Server version") 834 | endif() 835 | 836 | # Clean up so only numeric, in case of "-alpha" or similar 837 | string(REGEX MATCHALL "([0-9]+.[0-9]+.[0-9]+)" MYSQL_VERSION "${MYSQL_VERSION}") 838 | # To create a fully numeric version, first normalize so N.NN.NN 839 | string(REGEX REPLACE "[.]([0-9])[.]" ".0\\1." MYSQL_VERSION_ID "${MYSQL_VERSION}") 840 | string(REGEX REPLACE "[.]([0-9])$" ".0\\1" MYSQL_VERSION_ID "${MYSQL_VERSION_ID}") 841 | # Finally remove the dot 842 | string(REGEX REPLACE "[.]" "" MYSQL_VERSION_ID "${MYSQL_VERSION_ID}") 843 | set(MYSQL_NUM_VERSION ${MYSQL_VERSION_ID}) 844 | 845 | ########################################################################## 846 | # 847 | # Try determine if to use C++ linkage, and also find C++ flags 848 | # 849 | ########################################################################## 850 | 851 | if(NOT WIN32) 852 | 853 | if(MYSQL_CONFIG_EXECUTABLE) 854 | 855 | if(NOT MYSQL_CFLAGS) 856 | _mysql_conf(MYSQL_CFLAGS "--cflags") 857 | endif() 858 | 859 | if(NOT MYSQL_CXXFLAGS) 860 | if(MYSQL_CXX_LINKAGE OR MYSQL_VERSION_ID GREATER 50603) 861 | _mysql_conf(MYSQL_CXXFLAGS "--cxxflags") 862 | set(MYSQL_CXX_LINKAGE 1) 863 | else() 864 | set(MYSQL_CXXFLAGS "${MYSQL_CFLAGS}") 865 | endif() 866 | endif() 867 | 868 | # FIXME this should not be needed, caller of this module should set 869 | # it's own flags and just use the library on it's on terms 870 | # (change the infe message if enabling this code) 871 | # if(NOT MYSQL_LINK_FLAGS) 872 | # # Find -mcpu -march -mt -m32 -m64 and other flags starting with "-m" 873 | # string(REGEX MATCHALL "(^| )-m([^\r\n ]+)" MYSQL_LINK_FLAGS "${MYSQL_CXXFLAGS}") 874 | # string(REGEX REPLACE "^ " "" MYSQL_LINK_FLAGS "${MYSQL_LINK_FLAGS}") 875 | # string(REGEX REPLACE "; " ";" MYSQL_LINK_FLAGS "${MYSQL_LINK_FLAGS}") 876 | # endif() 877 | 878 | endif() 879 | 880 | endif() 881 | 882 | ########################################################################## 883 | # 884 | # Inform CMake where to look for headers and libraries 885 | # 886 | ########################################################################## 887 | 888 | # string(TOUPPER "${CMAKE_BUILD_TYPE}" CMAKEBT) 889 | # set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${MYSQL_CXXFLAGS}") 890 | # set(CMAKE_CXX_FLAGS_${CMAKEBT} "${CMAKE_CXX_FLAGS_${CMAKEBT}} ${MYSQL_CXXFLAGS}") 891 | 892 | include_directories("${MYSQL_INCLUDE_DIR}") 893 | 894 | link_directories("${MYSQL_LIB_DIR}") 895 | 896 | MESSAGE(STATUS "MYSQL_LIB_DIR_LIST = ${MYSQL_LIB_DIR_LIST}") 897 | IF(MYSQL_LIB_DIR_LIST) 898 | FOREACH(__libpath IN LISTS MYSQL_LIB_DIR_LIST) 899 | link_directories("${__libpath}") 900 | ENDFOREACH() 901 | ENDIF() 902 | 903 | 904 | 905 | ########################################################################## 906 | # 907 | # Report 908 | # 909 | ########################################################################## 910 | 911 | message(STATUS "MySQL client environment/cmake variables set that the user can override") 912 | 913 | message(STATUS " MYSQL_DIR : ${MYSQL_DIR}") 914 | message(STATUS " MYSQL_INCLUDE_DIR : ${MYSQL_INCLUDE_DIR}") 915 | message(STATUS " MYSQL_LIB_DIR : ${MYSQL_LIB_DIR}") 916 | message(STATUS " MYSQL_PLUGIN_DIR : ${MYSQL_PLUGIN_DIR}") 917 | message(STATUS " MYSQL_CONFIG_EXECUTABLE : ${MYSQL_CONFIG_EXECUTABLE}") 918 | message(STATUS " MYSQL_CXX_LINKAGE : ${MYSQL_CXX_LINKAGE}") 919 | message(STATUS " MYSQL_CFLAGS : ${MYSQL_CFLAGS}") 920 | message(STATUS " MYSQL_CXXFLAGS : ${MYSQL_CXXFLAGS}") 921 | message(STATUS " MYSQLCLIENT_STATIC_LINKING : ${MYSQLCLIENT_STATIC_LINKING}") 922 | message(STATUS " MYSQLCLIENT_NO_THREADS : ${MYSQLCLIENT_NO_THREADS}") 923 | 924 | message(STATUS "MySQL client optional environment/cmake variables set by the user") 925 | 926 | message(STATUS " MYSQL_EXTRA_LIBRARIES : ${MYSQL_EXTRA_LIBRARIES}") 927 | message(STATUS " MYSQL_LINK_FLAGS : ${MYSQL_LINK_FLAGS}") 928 | 929 | message(STATUS "MySQL client settings that the user can't override") 930 | 931 | message(STATUS " MYSQL_VERSION : ${MYSQL_VERSION}") 932 | message(STATUS " MYSQL_VERSION_ID : ${MYSQL_VERSION_ID}") 933 | message(STATUS " MYSQL_LIB : ${MYSQL_LIB}") 934 | message(STATUS " MYSQL_LIBRARIES : ${MYSQL_LIBRARIES}") 935 | --------------------------------------------------------------------------------