From ccb700f568bd858df1641846c6c90bdb329c4b81 Mon Sep 17 00:00:00 2001 From: Darby Johnston Date: Thu, 10 Sep 2026 15:43:11 -0700 Subject: [PATCH 1/2] Export the API correctly for shared builds on Windows OTIO_EXPORTS and OPENTIME_EXPORTS were PUBLIC, so everything consuming OTIO compiled its API as dllexport where it has to be dllimport. They become PRIVATE, which says the library is being built rather than consumed; the OTIO_STATIC and OPENTIME_STATIC definitions stay PUBLIC, since a consumer does have to know the API carries no declspec at all. With that corrected the members marked only at the class level came up missing, 74 of them, the Color statics and TypeRegistry among them: OTIO_API_TYPE is empty on Windows, where only the per-member OTIO_API carries the declspec. Those members are marked. The three find_children templates lose OTIO_API in turn, a template defined in a header being something that cannot be imported, and two source files that define exported functions without including the header that marks them are given the include. No change to what either library exports on macOS or Linux: the symbol tables of a shared build are identical before and after. Signed-off-by: Darby Johnston --- src/opentime/CMakeLists.txt | 2 +- src/opentimelineio/CMakeLists.txt | 2 +- src/opentimelineio/bundle.h | 2 +- src/opentimelineio/color.h | 34 +++++----- src/opentimelineio/composition.h | 2 +- src/opentimelineio/deserialization.cpp | 2 + src/opentimelineio/imageSequenceReference.h | 10 +-- src/opentimelineio/safely_typed_any.h | 63 ++++++++++--------- src/opentimelineio/serializableCollection.h | 2 +- src/opentimelineio/serializableObject.h | 16 ++--- .../serializableObjectWithMetadata.h | 6 +- src/opentimelineio/stringUtils.cpp | 2 + src/opentimelineio/stringUtils.h | 7 ++- src/opentimelineio/timeline.h | 4 +- src/opentimelineio/transition.h | 4 +- src/opentimelineio/typeRegistry.h | 18 +++--- 16 files changed, 91 insertions(+), 85 deletions(-) diff --git a/src/opentime/CMakeLists.txt b/src/opentime/CMakeLists.txt index 4f071060e..73cdcfb13 100644 --- a/src/opentime/CMakeLists.txt +++ b/src/opentime/CMakeLists.txt @@ -30,7 +30,7 @@ if(OTIO_SHARED_LIBS) VERSION ${OTIO_VERSION}) target_compile_definitions( opentime - PUBLIC + PRIVATE OPENTIME_EXPORTS) else() target_compile_definitions( diff --git a/src/opentimelineio/CMakeLists.txt b/src/opentimelineio/CMakeLists.txt index 994a493c2..be05a7c42 100644 --- a/src/opentimelineio/CMakeLists.txt +++ b/src/opentimelineio/CMakeLists.txt @@ -107,7 +107,7 @@ if(OTIO_SHARED_LIBS) VERSION ${OTIO_VERSION}) target_compile_definitions( opentimelineio - PUBLIC + PRIVATE OTIO_EXPORTS) else() target_compile_definitions( diff --git a/src/opentimelineio/bundle.h b/src/opentimelineio/bundle.h index daa46a7a3..d708ed375 100644 --- a/src/opentimelineio/bundle.h +++ b/src/opentimelineio/bundle.h @@ -37,7 +37,7 @@ namespace bundle { }; /// @brief Get a file from a URL. - std::optional file_from_url(std::string const& url); + OTIO_API std::optional file_from_url(std::string const& url); /// @brief Options for writing bundles. struct OTIO_API_TYPE WriteOptions diff --git a/src/opentimelineio/color.h b/src/opentimelineio/color.h index d20bf8707..48c3552be 100644 --- a/src/opentimelineio/color.h +++ b/src/opentimelineio/color.h @@ -39,23 +39,23 @@ class OTIO_API_TYPE Color OTIO_API Color(Color const& other); - static const Color pink; - static const Color red; - static const Color orange; - static const Color yellow; - static const Color green; - static const Color cyan; - static const Color blue; - static const Color purple; - static const Color magenta; - static const Color black; - static const Color white; - static const Color transparent; - - static Color* from_hex(std::string const& color); - static Color* from_int_list(std::vector const& color, int bit_depth); - static Color* from_agbr_int(unsigned int agbr) noexcept; - static Color* from_float_list(std::vector const& color); + OTIO_API static const Color pink; + OTIO_API static const Color red; + OTIO_API static const Color orange; + OTIO_API static const Color yellow; + OTIO_API static const Color green; + OTIO_API static const Color cyan; + OTIO_API static const Color blue; + OTIO_API static const Color purple; + OTIO_API static const Color magenta; + OTIO_API static const Color black; + OTIO_API static const Color white; + OTIO_API static const Color transparent; + + OTIO_API static Color* from_hex(std::string const& color); + OTIO_API static Color* from_int_list(std::vector const& color, int bit_depth); + OTIO_API static Color* from_agbr_int(unsigned int agbr) noexcept; + OTIO_API static Color* from_float_list(std::vector const& color); friend bool operator==(Color lhs, Color rhs) noexcept { diff --git a/src/opentimelineio/composition.h b/src/opentimelineio/composition.h index e2a72a470..4133c084f 100644 --- a/src/opentimelineio/composition.h +++ b/src/opentimelineio/composition.h @@ -157,7 +157,7 @@ class OTIO_API_TYPE Composition : public Item /// @param shallow_search The search is recursive unless shallow_search is /// set to true. template - OTIO_API std::vector> find_children( + std::vector> find_children( ErrorStatus* error_status = nullptr, std::optional search_range = std::nullopt, bool shallow_search = false) const; diff --git a/src/opentimelineio/deserialization.cpp b/src/opentimelineio/deserialization.cpp index 51f85c6c3..151cac946 100644 --- a/src/opentimelineio/deserialization.cpp +++ b/src/opentimelineio/deserialization.cpp @@ -1,6 +1,8 @@ // SPDX-License-Identifier: Apache-2.0 // Copyright Contributors to the OpenTimelineIO project +#include "opentimelineio/deserialization.h" + #include "opentime/rationalTime.h" #include "opentime/timeRange.h" #include "opentime/timeTransform.h" diff --git a/src/opentimelineio/imageSequenceReference.h b/src/opentimelineio/imageSequenceReference.h index 007a9f633..4ff1ead0d 100644 --- a/src/opentimelineio/imageSequenceReference.h +++ b/src/opentimelineio/imageSequenceReference.h @@ -136,23 +136,23 @@ class OTIO_API_TYPE ImageSequenceReference final : public MediaReference } /// @brief Return the end frame. - int end_frame() const; + OTIO_API int end_frame() const; /// @brief Return the number of images in the sequence. - int number_of_images_in_sequence() const; + OTIO_API int number_of_images_in_sequence() const; /// @brief Return the frame for the given time. - int frame_for_time( + OTIO_API int frame_for_time( RationalTime const& time, ErrorStatus* error_status = nullptr) const; /// @brief Return the target URL for the given image number. - std::string target_url_for_image_number( + OTIO_API std::string target_url_for_image_number( int image_number, ErrorStatus* error_status = nullptr) const; /// @brief Return the presentation time for the given image number. - RationalTime presentation_time_for_image_number( + OTIO_API RationalTime presentation_time_for_image_number( int image_number, ErrorStatus* error_status = nullptr) const; diff --git a/src/opentimelineio/safely_typed_any.h b/src/opentimelineio/safely_typed_any.h index f9586b96b..6aa615158 100644 --- a/src/opentimelineio/safely_typed_any.h +++ b/src/opentimelineio/safely_typed_any.h @@ -19,6 +19,7 @@ /// common library. That's why the seemingly silly code in safely_typed_any.cpp /// exists. +#include "opentimelineio/export.h" #include "opentime/rationalTime.h" #include "opentime/timeRange.h" #include "opentime/timeTransform.h" @@ -31,48 +32,48 @@ namespace opentimelineio { namespace OPENTIMELINEIO_VERSION_NS { /// @name Any Create ///@{ -std::any create_safely_typed_any(bool&&); -std::any create_safely_typed_any(int&&); -std::any create_safely_typed_any(int64_t&&); -std::any create_safely_typed_any(uint64_t&&); -std::any create_safely_typed_any(double&&); -std::any create_safely_typed_any(std::string&&); -std::any create_safely_typed_any(RationalTime&&); -std::any create_safely_typed_any(TimeRange&&); -std::any create_safely_typed_any(Color&&); -std::any create_safely_typed_any(TimeTransform&&); -std::any create_safely_typed_any(IMATH_NAMESPACE::V2d&&); -std::any create_safely_typed_any(IMATH_NAMESPACE::Box2d&&); -std::any create_safely_typed_any(AnyVector&&); -std::any create_safely_typed_any(AnyDictionary&&); -std::any create_safely_typed_any(SerializableObject*); +OTIO_API std::any create_safely_typed_any(bool&&); +OTIO_API std::any create_safely_typed_any(int&&); +OTIO_API std::any create_safely_typed_any(int64_t&&); +OTIO_API std::any create_safely_typed_any(uint64_t&&); +OTIO_API std::any create_safely_typed_any(double&&); +OTIO_API std::any create_safely_typed_any(std::string&&); +OTIO_API std::any create_safely_typed_any(RationalTime&&); +OTIO_API std::any create_safely_typed_any(TimeRange&&); +OTIO_API std::any create_safely_typed_any(Color&&); +OTIO_API std::any create_safely_typed_any(TimeTransform&&); +OTIO_API std::any create_safely_typed_any(IMATH_NAMESPACE::V2d&&); +OTIO_API std::any create_safely_typed_any(IMATH_NAMESPACE::Box2d&&); +OTIO_API std::any create_safely_typed_any(AnyVector&&); +OTIO_API std::any create_safely_typed_any(AnyDictionary&&); +OTIO_API std::any create_safely_typed_any(SerializableObject*); ///@} /// @name Any Casting ///@{ -bool safely_cast_bool_any(std::any const& a); -int safely_cast_int_any(std::any const& a); -int64_t safely_cast_int64_any(std::any const& a); -uint64_t safely_cast_uint64_any(std::any const& a); -double safely_cast_double_any(std::any const& a); -std::string safely_cast_string_any(std::any const& a); -RationalTime safely_cast_rational_time_any(std::any const& a); -TimeRange safely_cast_time_range_any(std::any const& a); -TimeTransform safely_cast_time_transform_any(std::any const& a); -Color safely_cast_color_any(std::any const& a); -IMATH_NAMESPACE::V2d safely_cast_point_any(std::any const& a); -IMATH_NAMESPACE::Box2d safely_cast_box_any(std::any const& a); +OTIO_API bool safely_cast_bool_any(std::any const& a); +OTIO_API int safely_cast_int_any(std::any const& a); +OTIO_API int64_t safely_cast_int64_any(std::any const& a); +OTIO_API uint64_t safely_cast_uint64_any(std::any const& a); +OTIO_API double safely_cast_double_any(std::any const& a); +OTIO_API std::string safely_cast_string_any(std::any const& a); +OTIO_API RationalTime safely_cast_rational_time_any(std::any const& a); +OTIO_API TimeRange safely_cast_time_range_any(std::any const& a); +OTIO_API TimeTransform safely_cast_time_transform_any(std::any const& a); +OTIO_API Color safely_cast_color_any(std::any const& a); +OTIO_API IMATH_NAMESPACE::V2d safely_cast_point_any(std::any const& a); +OTIO_API IMATH_NAMESPACE::Box2d safely_cast_box_any(std::any const& a); -SerializableObject* safely_cast_retainer_any(std::any const& a); +OTIO_API SerializableObject* safely_cast_retainer_any(std::any const& a); -AnyDictionary safely_cast_any_dictionary_any(std::any const& a); +OTIO_API AnyDictionary safely_cast_any_dictionary_any(std::any const& a); AnyVector safely_cast_any_vector_any(std::any const& a); /// @bug Don't use these unless you know what you're doing... -AnyDictionary& temp_safely_cast_any_dictionary_any(std::any const& a); -AnyVector& temp_safely_cast_any_vector_any(std::any const& a); +OTIO_API AnyDictionary& temp_safely_cast_any_dictionary_any(std::any const& a); +OTIO_API AnyVector& temp_safely_cast_any_vector_any(std::any const& a); ///@} diff --git a/src/opentimelineio/serializableCollection.h b/src/opentimelineio/serializableCollection.h index cf2d0087f..d0760e557 100644 --- a/src/opentimelineio/serializableCollection.h +++ b/src/opentimelineio/serializableCollection.h @@ -98,7 +98,7 @@ class OTIO_API_TYPE SerializableCollection /// @param shallow_search The search is recursive unless shallow_search is /// set to true. template - OTIO_API std::vector> find_children( + std::vector> find_children( ErrorStatus* error_status = nullptr, std::optional search_range = std::nullopt, bool shallow_search = false) const; diff --git a/src/opentimelineio/serializableObject.h b/src/opentimelineio/serializableObject.h index 4f2e6c6b6..91d8d5c7c 100644 --- a/src/opentimelineio/serializableObject.h +++ b/src/opentimelineio/serializableObject.h @@ -604,13 +604,13 @@ class OTIO_API_TYPE SerializableObject }; /// @brief Deserialize from the given reader. - virtual bool read_from(Reader&); + OTIO_API virtual bool read_from(Reader&); /// @brief Serialize to the given writer. - virtual void write_to(Writer&) const; + OTIO_API virtual void write_to(Writer&) const; /// @brief Return whether this schema is unknown. - virtual bool is_unknown_schema() const; + OTIO_API virtual bool is_unknown_schema() const; /// @brief Return the schema name. std::string schema_name() const { return _type_record()->schema_name; } @@ -675,9 +675,9 @@ class OTIO_API_TYPE SerializableObject protected: virtual ~SerializableObject(); - virtual bool _is_deletable(); + OTIO_API virtual bool _is_deletable(); - virtual std::string _schema_name_for_reference() const; + OTIO_API virtual std::string _schema_name_for_reference() const; private: SerializableObject(SerializableObject const&) = delete; @@ -700,12 +700,12 @@ class OTIO_API_TYPE SerializableObject }; /// @todo Add comment. - void install_external_keepalive_monitor( + OTIO_API void install_external_keepalive_monitor( std::function monitor, bool apply_now); /// @brief Return the current reference count. - int current_ref_count() const; + OTIO_API int current_ref_count() const; /// @brief This struct provides an unknown type. struct UnknownType @@ -719,7 +719,7 @@ class OTIO_API_TYPE SerializableObject _cached_type_record = type_record; } - TypeRegistry::_TypeRecord const* _type_record() const; + OTIO_API TypeRegistry::_TypeRecord const* _type_record() const; mutable TypeRegistry::_TypeRecord const* _cached_type_record; int _managed_ref_count; diff --git a/src/opentimelineio/serializableObjectWithMetadata.h b/src/opentimelineio/serializableObjectWithMetadata.h index 4eeac3f5e..a7e558c62 100644 --- a/src/opentimelineio/serializableObjectWithMetadata.h +++ b/src/opentimelineio/serializableObjectWithMetadata.h @@ -42,10 +42,10 @@ class OTIO_API_TYPE SerializableObjectWithMetadata : public SerializableObject AnyDictionary metadata() const noexcept { return _metadata; } protected: - virtual ~SerializableObjectWithMetadata(); + OTIO_API virtual ~SerializableObjectWithMetadata(); - bool read_from(Reader&) override; - void write_to(Writer&) const override; + OTIO_API bool read_from(Reader&) override; + OTIO_API void write_to(Writer&) const override; private: std::string _name; diff --git a/src/opentimelineio/stringUtils.cpp b/src/opentimelineio/stringUtils.cpp index 20ac2c2ce..c6b605191 100644 --- a/src/opentimelineio/stringUtils.cpp +++ b/src/opentimelineio/stringUtils.cpp @@ -1,6 +1,8 @@ // SPDX-License-Identifier: Apache-2.0 // Copyright Contributors to the OpenTimelineIO project +#include "stringUtils.h" + #include "opentimelineio/serializableObject.h" #include #include diff --git a/src/opentimelineio/stringUtils.h b/src/opentimelineio/stringUtils.h index 709b5c1b8..aee94a277 100644 --- a/src/opentimelineio/stringUtils.h +++ b/src/opentimelineio/stringUtils.h @@ -4,6 +4,7 @@ #pragma once #include "opentime/stringPrintf.h" +#include "opentimelineio/export.h" #include "opentimelineio/version.h" using opentime::string_printf; @@ -17,9 +18,9 @@ namespace opentimelineio { namespace OPENTIMELINEIO_VERSION_NS { void fatal_error(std::string const& errMsg); -std::string type_name_for_error_message(std::type_info const&); -std::string type_name_for_error_message(std::any const& a); -std::string type_name_for_error_message(class SerializableObject*); +OTIO_API std::string type_name_for_error_message(std::type_info const&); +OTIO_API std::string type_name_for_error_message(std::any const& a); +OTIO_API std::string type_name_for_error_message(class SerializableObject*); template std::string diff --git a/src/opentimelineio/timeline.h b/src/opentimelineio/timeline.h index 9273ad5bf..470dcff5a 100644 --- a/src/opentimelineio/timeline.h +++ b/src/opentimelineio/timeline.h @@ -44,7 +44,7 @@ class OTIO_API_TYPE Timeline : public SerializableObjectWithMetadata }*/ /// @brief Set the timeline stack. - void set_tracks(Stack* stack); + OTIO_API void set_tracks(Stack* stack); /// @brief Return the global start time. std::optional global_start_time() const noexcept @@ -97,7 +97,7 @@ class OTIO_API_TYPE Timeline : public SerializableObjectWithMetadata /// @param shallow_search The search is recursive unless shallow_search is /// set to true. template - OTIO_API std::vector> find_children( + std::vector> find_children( ErrorStatus* error_status = nullptr, std::optional search_range = std::nullopt, bool shallow_search = false) const; diff --git a/src/opentimelineio/transition.h b/src/opentimelineio/transition.h index 2dd75cf15..187eb7850 100644 --- a/src/opentimelineio/transition.h +++ b/src/opentimelineio/transition.h @@ -82,11 +82,11 @@ class OTIO_API_TYPE Transition : public Composable RationalTime duration(ErrorStatus* error_status = nullptr) const override; /// @brief Return the range in the parent's time. - std::optional + OTIO_API std::optional range_in_parent(ErrorStatus* error_status = nullptr) const; /// @brief Return the range trimmed in the parent's time. - std::optional + OTIO_API std::optional trimmed_range_in_parent(ErrorStatus* error_status = nullptr) const; protected: diff --git a/src/opentimelineio/typeRegistry.h b/src/opentimelineio/typeRegistry.h index 2f9182ac9..83ea5bdaa 100644 --- a/src/opentimelineio/typeRegistry.h +++ b/src/opentimelineio/typeRegistry.h @@ -33,7 +33,7 @@ using label_to_schema_version_map = ///@} -extern const label_to_schema_version_map CORE_VERSION_MAP; +extern OTIO_API const label_to_schema_version_map CORE_VERSION_MAP; /// @brief Type registry. class OTIO_API_TYPE TypeRegistry @@ -42,7 +42,7 @@ class OTIO_API_TYPE TypeRegistry /// @brief Get the type registry singleton. /// /// Access to functions are thread-safe. - static TypeRegistry& instance(); + OTIO_API static TypeRegistry& instance(); /// @brief Register a new schema. /// @@ -51,7 +51,7 @@ class OTIO_API_TYPE TypeRegistry /// the templated form of this call. /// /// If the specified schema_name has already been registered, this function does nothing and returns false. - bool register_type( + OTIO_API bool register_type( std::string const& schema_name, int schema_version, std::type_info const* type, @@ -79,7 +79,7 @@ class OTIO_API_TYPE TypeRegistry /// case a schema name is changed and the old name needs to be allowed as well. /// /// On success, returns true; otherwise, returns false and sets error_status if non-null. - bool register_type_from_existing_type( + OTIO_API bool register_type_from_existing_type( std::string const& schema_name, int schema_version, std::string const& existing_schema_name, @@ -98,7 +98,7 @@ class OTIO_API_TYPE TypeRegistry /// /// Returns false if an upgrade function has been registered for this (schema_name, version) /// pair, or if schema_name itself has not been registered, and true otherwise. - bool register_upgrade_function( + OTIO_API bool register_upgrade_function( std::string const& schema_name, int version_to_upgrade_to, std::function upgrade_function); @@ -119,7 +119,7 @@ class OTIO_API_TYPE TypeRegistry /// @brief Downgrade function from version_to_downgrade_from to /// version_to_downgrade_from - 1 - bool register_downgrade_function( + OTIO_API bool register_downgrade_function( std::string const& schema_name, int version_to_downgrade_from, std::function downgrade_function); @@ -154,13 +154,13 @@ class OTIO_API_TYPE TypeRegistry } /// @brief For use by external bridging systems. - bool set_type_record( + OTIO_API bool set_type_record( SerializableObject*, std::string const& schema_name, ErrorStatus* error_status = nullptr); /// @brief For inspecting the type registry, build a map of schema name to version. - void type_version_map(schema_version_map& result); + OTIO_API void type_version_map(schema_version_map& result); private: TypeRegistry(); @@ -204,7 +204,7 @@ class OTIO_API_TYPE TypeRegistry return it == _type_records.end() ? nullptr : it->second; } - SerializableObject* _instance_from_schema( + OTIO_API SerializableObject* _instance_from_schema( std::string schema_name, int schema_version, AnyDictionary& dict, From 98b84aa6600c07208fddc0243c121c64ad185103 Mon Sep 17 00:00:00 2001 From: Darby Johnston Date: Mon, 21 Sep 2026 13:50:57 -0700 Subject: [PATCH 2/2] Export the rest of what the examples link against SerializableObject's destructor, its Reader's any overload and type check, and its Writer's int64_t overload had no OTIO_API on them. A build that hides its symbols by default, which main does since #2041, leaves them out of the library and upgrade_downgrade_example cannot be linked. Signed-off-by: Darby Johnston --- src/opentimelineio/serializableObject.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/opentimelineio/serializableObject.h b/src/opentimelineio/serializableObject.h index 91d8d5c7c..e809117e3 100644 --- a/src/opentimelineio/serializableObject.h +++ b/src/opentimelineio/serializableObject.h @@ -132,7 +132,7 @@ class OTIO_API_TYPE SerializableObject bool read(std::string const& key, IMATH_NAMESPACE::Box2d* value); bool read(std::string const& key, AnyVector* dest); bool read(std::string const& key, AnyDictionary* dest); - bool read(std::string const& key, std::any* dest); + OTIO_API bool read(std::string const& key, std::any* dest); bool read(std::string const& key, std::optional* dest); bool read(std::string const& key, std::optional* dest); @@ -402,7 +402,7 @@ class OTIO_API_TYPE SerializableObject bool _fetch(std::string const& key, int64_t* dest); bool _fetch(std::string const& key, double* dest); bool _fetch(std::string const& key, SerializableObject** dest); - bool + OTIO_API bool _type_check(std::type_info const& wanted, std::type_info const& found); bool _type_check_so( std::type_info const& wanted, @@ -446,7 +446,7 @@ class OTIO_API_TYPE SerializableObject ErrorStatus* error_status = nullptr); void write(std::string const& key, bool value); - void write(std::string const& key, int64_t value); + OTIO_API void write(std::string const& key, int64_t value); void write(std::string const& key, double value); void write(std::string const& key, std::string const& value); void write(std::string const& key, RationalTime value); @@ -673,7 +673,7 @@ class OTIO_API_TYPE SerializableObject }; protected: - virtual ~SerializableObject(); + OTIO_API virtual ~SerializableObject(); OTIO_API virtual bool _is_deletable();