Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Build Yoga 2.0 as a shared library (partial implementation). #1341

Open
ghost opened this issue Aug 8, 2023 · 15 comments
Open

Build Yoga 2.0 as a shared library (partial implementation). #1341

ghost opened this issue Aug 8, 2023 · 15 comments

Comments

@ghost
Copy link

ghost commented Aug 8, 2023

I need to build Yoga as a shared library so that I can create C# bindings using p/invoke. Below is the updated CMakeLists.txt file for the 'yoga' directory. It includes an option to build Yoga as a shared library. Please note that although it successfully builds Yoga as a working shared library, it still throws an error. I suspect the issue is with yogatest trying to build against the Yoga static library. Any help with working on this would be greatly appreciated.

Error

collect2: error: ld returned 1 exit status
make[2]: *** [tests/CMakeFiles/yogatests.dir/build.make:887: tests/yogatests] Error 1
make[2]: Leaving directory '/home/sstarr/Downloads/yoga-2.0.0/build'
make[1]: *** [CMakeFiles/Makefile2:199: tests/CMakeFiles/yogatests.dir/all] Error 2
make[1]: Leaving directory '/home/sstarr/Downloads/yoga-2.0.0/build'
make: *** [Makefile:139: all] Error 2

yoga/CMakeLists.txt

# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.


cmake_minimum_required(VERSION 3.13...3.26)
project(yogacore)
set(CMAKE_VERBOSE_MAKEFILE on)

if(TARGET yogacore)
    return()
endif()

include(CheckIPOSupported)

# Define an option to choose between shared and static build
option(BUILD_SHARED_LIBS "Build yogacore as a shared library" OFF)

set(YOGA_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/..)
include(${YOGA_ROOT}/cmake/project-defaults.cmake)

file(GLOB SOURCES CONFIGURE_DEPENDS
    ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp
    ${CMAKE_CURRENT_SOURCE_DIR}/**/*.cpp)

# Choose between adding a shared or static library based on the BUILD_SHARED_LIBS option
if(BUILD_SHARED_LIBS)
    add_library(yogacore SHARED ${SOURCES})
else()
    add_library(yogacore STATIC ${SOURCES})
endif()

# Yoga conditionally uses <android/log> when building for Android
if (ANDROID)
    target_link_libraries(yogacore log)
endif()

check_ipo_supported(RESULT result)
if(result)
    set_target_properties(yogacore PROPERTIES
        CMAKE_INTERPROCEDURAL_OPTIMIZATION true)
endif()

target_include_directories(yogacore
    PUBLIC
    $<BUILD_INTERFACE:${YOGA_ROOT}>
    $<INSTALL_INTERFACE:${CMAKE_INSTALL_PREFIX}/include/yoga>)
@NickGerleman
Copy link
Contributor

NickGerleman commented Aug 8, 2023

It’s hard to tell what the issue is with the limited error output.

Instead of changing yogacore to be either a shared or static library, it might be easier to link the static library into a new shared library. As a reference, the JNI bindings do just this, and link in some more code which is binding specific to a final libyoga.so. https://github.com/facebook/yoga/blob/main/java/CMakeLists.txt.

@ghost
Copy link
Author

ghost commented Aug 8, 2023

I was able to get this buy using make VERBOSE=1 and it looks like a linking error related to the undefined reference to YGFloatOptionalMax(YGFloatOptional, YGFloatOptional)

[ 90%] Building CXX object tests/CMakeFiles/yogatests.dir/generated/YGRoundingTest.cpp.o
cd /home/sstarr/Downloads/yoga-2.0.0/build/tests && /usr/bin/c++  -I/home/sstarr/Downloads/yoga-2.0.0/yoga/.. -isystem /home/sstarr/Downloads/yoga-2.0.0/build/_deps/googletest-src/googletest/include -isystem /home/sstarr/Downloads/yoga-2.0.0/build/_deps/googletest-src/googletest -fPIE -fvisibility=hidden -fno-omit-frame-pointer -fexceptions -Wall -Wextra -Werror -fno-rtti -std=gnu++14 -MD -MT tests/CMakeFiles/yogatests.dir/generated/YGRoundingTest.cpp.o -MF CMakeFiles/yogatests.dir/generated/YGRoundingTest.cpp.o.d -o CMakeFiles/yogatests.dir/generated/YGRoundingTest.cpp.o -c /home/sstarr/Downloads/yoga-2.0.0/tests/generated/YGRoundingTest.cpp
[ 91%] Building CXX object tests/CMakeFiles/yogatests.dir/generated/YGSizeOverflowTest.cpp.o
cd /home/sstarr/Downloads/yoga-2.0.0/build/tests && /usr/bin/c++  -I/home/sstarr/Downloads/yoga-2.0.0/yoga/.. -isystem /home/sstarr/Downloads/yoga-2.0.0/build/_deps/googletest-src/googletest/include -isystem /home/sstarr/Downloads/yoga-2.0.0/build/_deps/googletest-src/googletest -fPIE -fvisibility=hidden -fno-omit-frame-pointer -fexceptions -Wall -Wextra -Werror -fno-rtti -std=gnu++14 -MD -MT tests/CMakeFiles/yogatests.dir/generated/YGSizeOverflowTest.cpp.o -MF CMakeFiles/yogatests.dir/generated/YGSizeOverflowTest.cpp.o.d -o CMakeFiles/yogatests.dir/generated/YGSizeOverflowTest.cpp.o -c /home/sstarr/Downloads/yoga-2.0.0/tests/generated/YGSizeOverflowTest.cpp
[ 92%] Building CXX object tests/CMakeFiles/yogatests.dir/util/TestUtil.cpp.o
cd /home/sstarr/Downloads/yoga-2.0.0/build/tests && /usr/bin/c++  -I/home/sstarr/Downloads/yoga-2.0.0/yoga/.. -isystem /home/sstarr/Downloads/yoga-2.0.0/build/_deps/googletest-src/googletest/include -isystem /home/sstarr/Downloads/yoga-2.0.0/build/_deps/googletest-src/googletest -fPIE -fvisibility=hidden -fno-omit-frame-pointer -fexceptions -Wall -Wextra -Werror -fno-rtti -std=gnu++14 -MD -MT tests/CMakeFiles/yogatests.dir/util/TestUtil.cpp.o -MF CMakeFiles/yogatests.dir/util/TestUtil.cpp.o.d -o CMakeFiles/yogatests.dir/util/TestUtil.cpp.o -c /home/sstarr/Downloads/yoga-2.0.0/tests/util/TestUtil.cpp
[ 94%] Linking CXX executable yogatests
cd /home/sstarr/Downloads/yoga-2.0.0/build/tests && /usr/bin/cmake -E cmake_link_script CMakeFiles/yogatests.dir/link.txt --verbose=1
/usr/bin/c++ CMakeFiles/yogatests.dir/BitUtilsTest.cpp.o CMakeFiles/yogatests.dir/CompactValueTest.cpp.o CMakeFiles/yogatests.dir/EventsTest.cpp.o CMakeFiles/yogatests.dir/YGAlignBaselineTest.cpp.o CMakeFiles/yogatests.dir/YGAspectRatioTest.cpp.o CMakeFiles/yogatests.dir/YGBaselineFuncTest.cpp.o CMakeFiles/yogatests.dir/YGComputedMarginTest.cpp.o CMakeFiles/yogatests.dir/YGComputedPaddingTest.cpp.o CMakeFiles/yogatests.dir/YGDefaultValuesTest.cpp.o CMakeFiles/yogatests.dir/YGDirtiedTest.cpp.o CMakeFiles/yogatests.dir/YGDirtyMarkingTest.cpp.o CMakeFiles/yogatests.dir/YGEdgeTest.cpp.o CMakeFiles/yogatests.dir/YGFloatOptionalTest.cpp.o CMakeFiles/yogatests.dir/YGHadOverflowTest.cpp.o CMakeFiles/yogatests.dir/YGLoggerTest.cpp.o CMakeFiles/yogatests.dir/YGMeasureCacheTest.cpp.o CMakeFiles/yogatests.dir/YGMeasureModeTest.cpp.o CMakeFiles/yogatests.dir/YGMeasureTest.cpp.o CMakeFiles/yogatests.dir/YGNodeCallbackTest.cpp.o CMakeFiles/yogatests.dir/YGNodeChildTest.cpp.o CMakeFiles/yogatests.dir/YGPersistenceTest.cpp.o CMakeFiles/yogatests.dir/YGRelayoutTest.cpp.o CMakeFiles/yogatests.dir/YGRoundingFunctionTest.cpp.o CMakeFiles/yogatests.dir/YGRoundingMeasureFuncTest.cpp.o CMakeFiles/yogatests.dir/YGStyleAccessorsTest.cpp.o CMakeFiles/yogatests.dir/YGStyleTest.cpp.o CMakeFiles/yogatests.dir/YGTreeMutationTest.cpp.o CMakeFiles/yogatests.dir/YGValueTest.cpp.o CMakeFiles/yogatests.dir/YGZeroOutLayoutRecursivlyTest.cpp.o CMakeFiles/yogatests.dir/generated/YGAbsolutePositionTest.cpp.o CMakeFiles/yogatests.dir/generated/YGAlignContentTest.cpp.o CMakeFiles/yogatests.dir/generated/YGAlignItemsTest.cpp.o CMakeFiles/yogatests.dir/generated/YGAlignSelfTest.cpp.o CMakeFiles/yogatests.dir/generated/YGAndroidNewsFeed.cpp.o CMakeFiles/yogatests.dir/generated/YGBorderTest.cpp.o CMakeFiles/yogatests.dir/generated/YGConfigTest.cpp.o CMakeFiles/yogatests.dir/generated/YGDimensionTest.cpp.o CMakeFiles/yogatests.dir/generated/YGDisplayTest.cpp.o CMakeFiles/yogatests.dir/generated/YGFlexDirectionTest.cpp.o CMakeFiles/yogatests.dir/generated/YGFlexTest.cpp.o CMakeFiles/yogatests.dir/generated/YGFlexWrapTest.cpp.o CMakeFiles/yogatests.dir/generated/YGGapTest.cpp.o CMakeFiles/yogatests.dir/generated/YGJustifyContentTest.cpp.o CMakeFiles/yogatests.dir/generated/YGMarginTest.cpp.o CMakeFiles/yogatests.dir/generated/YGMinMaxDimensionTest.cpp.o CMakeFiles/yogatests.dir/generated/YGPaddingTest.cpp.o CMakeFiles/yogatests.dir/generated/YGPercentageTest.cpp.o CMakeFiles/yogatests.dir/generated/YGRoundingTest.cpp.o CMakeFiles/yogatests.dir/generated/YGSizeOverflowTest.cpp.o CMakeFiles/yogatests.dir/util/TestUtil.cpp.o -o yogatests  -Wl,-rpath,/home/sstarr/Downloads/yoga-2.0.0/build/yoga:/home/sstarr/Downloads/yoga-2.0.0/build/lib ../yoga/libyogacore.so ../lib/libgtest_main.so.1.12.1 ../lib/libgtest.so.1.12.1 
/usr/bin/ld: CMakeFiles/yogatests.dir/YGFloatOptionalTest.cpp.o: in function `YGFloatOptionalTest_YGFloatOptionalMax_Test::TestBody()':
YGFloatOptionalTest.cpp:(.text+0xb413): undefined reference to `YGFloatOptionalMax(YGFloatOptional, YGFloatOptional)'
/usr/bin/ld: YGFloatOptionalTest.cpp:(.text+0xb50d): undefined reference to `YGFloatOptionalMax(YGFloatOptional, YGFloatOptional)'
/usr/bin/ld: YGFloatOptionalTest.cpp:(.text+0xb5f8): undefined reference to `YGFloatOptionalMax(YGFloatOptional, YGFloatOptional)'
/usr/bin/ld: YGFloatOptionalTest.cpp:(.text+0xb6f6): undefined reference to `YGFloatOptionalMax(YGFloatOptional, YGFloatOptional)'
/usr/bin/ld: YGFloatOptionalTest.cpp:(.text+0xb81d): undefined reference to `YGFloatOptionalMax(YGFloatOptional, YGFloatOptional)'
collect2: error: ld returned 1 exit status
make[2]: *** [tests/CMakeFiles/yogatests.dir/build.make:887: tests/yogatests] Error 1
make[2]: Leaving directory '/home/sstarr/Downloads/yoga-2.0.0/build'
make[1]: *** [CMakeFiles/Makefile2:218: tests/CMakeFiles/yogatests.dir/all] Error 2
make[1]: Leaving directory '/home/sstarr/Downloads/yoga-2.0.0/build'
make: *** [Makefile:139: all] Error 2
[sstarr@desktop-59bitp3 build]$ make VERBOSE=1

@ghost
Copy link
Author

ghost commented Aug 8, 2023

I found the following entry in the shared library:

0000000000006bd0 t _Z18YGFloatOptionalMax15YGFloatOptionalS_

by executing nm libyogacore.so > shared_symbols.txt. According to the documentation for the nm command, the t indicates that this symbol is in the text (code) section of the shared library and has local linkage. This means that the symbol is not visible outside the shared library itself, explaining the undefined reference error I encountered when linking against it in yogatests.

@ghost
Copy link
Author

ghost commented Aug 8, 2023

I found the problem in the Utils.h file on line 72. I changed this code:

YGFloatOptional YGFloatOptionalMax(
    const YGFloatOptional op1,
    const YGFloatOptional op2);

to:

__attribute__((visibility("default"))) YGFloatOptional YGFloatOptionalMax(
    const YGFloatOptional op1,
    const YGFloatOptional op2);

Now everything builds correctly. I would really appreciate it if the Yoga developers could conduct their testing, and possibly include an option to build a shared library in a future release.

@ghost
Copy link
Author

ghost commented Aug 8, 2023

It’s hard to tell what the issue is with the limited error output.

Instead of changing yogacore to be either a shared or static library, it might be easier to link the static library into a new shared library. As a reference, the JNI bindings do just this, and link in some more code which is binding specific to a final libyoga.so. https://github.com/facebook/yoga/blob/main/java/CMakeLists.txt.

I just realized we are neighbors; I am in Bremerton, WA. ;)

@ghost
Copy link
Author

ghost commented Aug 8, 2023

I should also note that what I did, I believe, only works when building using the GCC compiler, maybe LLVM, but I'm not sure. A solution that works for all supported compilers would probably look different than what I did above. When I have time, I'll look into Windows and MacOS (Intel), but mobile and Apple Silicon are beyond my capabilities. But the above at-least works on Linux.

Utils.h

// Static build regardless should work fine.
#if defined(__GNUC__) // Shared build using gcc will work.
__attribute__((visibility("default"))) YGFloatOptional YGFloatOptionalMax(
    const YGFloatOptional op1,
    const YGFloatOptional op2);
#else
// Shared build using any other compiler might fail
YGFloatOptional YGFloatOptionalMax(
    const YGFloatOptional op1,
    const YGFloatOptional op2);
#endif

@NickGerleman
Copy link
Contributor

There are macros like YG_EXPORT for defining what functions should be exported out of a shared library. Though it seems like unit tests are potentially poking at Yoga internals. I think it is reasonable that the unit tests are able to access APIs we would ideally not export from the library boundary. I.e. I think it might best to keep GTest binary statically linked to Yoga.

@ghost
Copy link
Author

ghost commented Aug 9, 2023

There are macros like YG_EXPORT for defining what functions should be exported out of a shared library. Though it seems like unit tests are potentially poking at Yoga internals. I think it is reasonable that the unit tests are able to access APIs we would ideally not export from the library boundary. I.e. I think it might best to keep GTest binary statically linked to Yoga.

As far as I can tell from scanning the code base, there is no YG_EXPORT already defined, so it would need to be created. Regarding the decision to keep the GTest binary statically linked, I completely disagree. The simple fact is that it doesn't matter either way because GTest isn't getting deployed to the end user, and it serves no other purpose outside of testing the API.

@NickGerleman
Copy link
Contributor

Oh, it looks like the export macros are YOGA_EXPORT and WIN_EXPORT. I don't see why these are separate right now on first glance. These also break the convention of the other macros starting with YG_ https://github.com/facebook/yoga/blob/main/yoga/YGMacros.h

The downside of dynamically linking GTest, apart from just the added build config, is that it means anything the UT consumes must be exported from Yoga. This means we need to expose functions in the export table, that were previously considered private. Though if there aren't too many extra exports, and they are safe to be public, I think we could add them.

@ghost
Copy link
Author

ghost commented Aug 9, 2023

Thanks for pointing that out. It's crazy that I missed YGMacros.h and ...

#ifdef _WINDLL
#define WIN_EXPORT __declspec(dllexport)
#else
#define WIN_EXPORT
#endif

#ifndef YOGA_EXPORT
#ifdef _MSC_VER
#define YOGA_EXPORT
#else
#define YOGA_EXPORT __attribute__((visibility("default")))
#endif
#endif

@ghost
Copy link
Author

ghost commented Aug 9, 2023

I made the following modification to Utils.h, which resulted in everything functioning correctly on Linux and MacOS. However, when attempting to build the shared library on Windows 10 using CMake and Visual Studio 2022, an error arises during the yogatest building stage.

Utils.h

#if defined(_WINDLL)
#define EXPORT WIN_EXPORT
#else
#define EXPORT YOGA_EXPORT
#endif

EXPORT YGFloatOptional YGFloatOptionalMax(
    const YGFloatOptional op1,
    const YGFloatOptional op2);
Target Link:
7>  CompactValueTest.obj : error LNK2001: unresolved external symbol YGValueAuto
7>  CompactValueTest.obj : error LNK2001: unresolved external symbol YGValueUndefined
7>  EventsTest.obj : error LNK2019: unresolved external symbol YGNodeSetBaselineFunc referenced in function "private: virtual void __cdecl facebook::yoga::test::EventTest_baseline_functions_get_wrapped_Test::TestBody(void)" (?TestBody@EventTest_baseline_functions_get_wrapped_Test@test@yoga@facebook@@EEAAXXZ)
7>  YGAlignBaselineTest.obj : error LNK2001: unresolved external symbol YGNodeSetBaselineFunc
7>  YGNodeCallbackTest.obj : error LNK2001: unresolved external symbol "public: void __cdecl YGNode::setMeasureFunc(struct YGSize (__cdecl*)(struct YGNode *,float,enum YGMeasureMode,float,enum YGMeasureMode))" (?setMeasureFunc@YGNode@@QEAAXP6A?AUYGSize@@PEAU1@MW4YGMeasureMode@@M1@Z@Z)
7>  YGRoundingMeasureFuncTest.obj : error LNK2001: unresolved external symbol "public: void __cdecl YGNode::setMeasureFunc(struct YGSize (__cdecl*)(struct YGNode *,float,enum YGMeasureMode,float,enum YGMeasureMode))" (?setMeasureFunc@YGNode@@QEAAXP6A?AUYGSize@@PEAU1@MW4YGMeasureMode@@M1@Z@Z)
7>  YGAspectRatioTest.obj : error LNK2001: unresolved external symbol "public: void __cdecl YGNode::setMeasureFunc(struct YGSize (__cdecl*)(struct YGNode *,float,enum YGMeasureMode,float,enum YGMeasureMode))" (?setMeasureFunc@YGNode@@QEAAXP6A?AUYGSize@@PEAU1@MW4YGMeasureMode@@M1@Z@Z)
7>  YGMeasureCacheTest.obj : error LNK2001: unresolved external symbol "public: void __cdecl YGNode::setMeasureFunc(struct YGSize (__cdecl*)(struct YGNode *,float,enum YGMeasureMode,float,enum YGMeasureMode))" (?setMeasureFunc@YGNode@@QEAAXP6A?AUYGSize@@PEAU1@MW4YGMeasureMode@@M1@Z@Z)
7>  YGMeasureModeTest.obj : error LNK2001: unresolved external symbol "public: void __cdecl YGNode::setMeasureFunc(struct YGSize (__cdecl*)(struct YGNode *,float,enum YGMeasureMode,float,enum YGMeasureMode))" (?setMeasureFunc@YGNode@@QEAAXP6A?AUYGSize@@PEAU1@MW4YGMeasureMode@@M1@Z@Z)
7>  YGMeasureTest.obj : error LNK2001: unresolved external symbol "public: void __cdecl YGNode::setMeasureFunc(struct YGSize (__cdecl*)(struct YGNode *,float,enum YGMeasureMode,float,enum YGMeasureMode))" (?setMeasureFunc@YGNode@@QEAAXP6A?AUYGSize@@PEAU1@MW4YGMeasureMode@@M1@Z@Z)
7>  YGDirtiedTest.obj : error LNK2019: unresolved external symbol "public: void __cdecl YGNode::setDirty(bool)" (?setDirty@YGNode@@QEAAX_N@Z) referenced in function "private: virtual void __cdecl YogaTest_dirtied_Test::TestBody(void)" (?TestBody@YogaTest_dirtied_Test@@EEAAXXZ)
7>  YGDirtiedTest.obj : error LNK2019: unresolved external symbol "public: void __cdecl YGNode::markDirtyAndPropagate(void)" (?markDirtyAndPropagate@YGNode@@QEAAXXZ) referenced in function "private: virtual void __cdecl YogaTest_dirtied_propagation_Test::TestBody(void)" (?TestBody@YogaTest_dirtied_propagation_Test@@EEAAXXZ)
7>  YGLoggerTest.obj : error LNK2019: unresolved external symbol YGConfigSetPrintTreeFlag referenced in function "private: virtual void __cdecl YogaTest_config_print_tree_enabled_Test::TestBody(void)" (?TestBody@YogaTest_config_print_tree_enabled_Test@@EEAAXXZ)
7>  YGNodeCallbackTest.obj : error LNK2019: unresolved external symbol "public: __cdecl YGNode::YGNode(struct YGConfig * const)" (??0YGNode@@QEAA@QEAUYGConfig@@@Z) referenced in function "public: __cdecl YGNode::YGNode(void)" (??0YGNode@@QEAA@XZ)
7>  YGConfigTest.obj : error LNK2001: unresolved external symbol "public: __cdecl YGNode::YGNode(struct YGConfig * const)" (??0YGNode@@QEAA@QEAUYGConfig@@@Z)
7>  YGNodeCallbackTest.obj : error LNK2019: unresolved external symbol "public: struct YGSize __cdecl YGNode::measure(float,enum YGMeasureMode,float,enum YGMeasureMode,void *)" (?measure@YGNode@@QEAA?AUYGSize@@MW4YGMeasureMode@@M0PEAX@Z) referenced in function "private: virtual void __cdecl YGNode_measure_with_measure_fn_Test::TestBody(void)" (?TestBody@YGNode_measure_with_measure_fn_Test@@EEAAXXZ)
7>  YGNodeCallbackTest.obj : error LNK2019: unresolved external symbol "public: float __cdecl YGNode::baseline(float,float,void *)" (?baseline@YGNode@@QEAAMMMPEAX@Z) referenced in function "private: virtual void __cdecl YGNode_baseline_with_baseline_fn_Test::TestBody(void)" (?TestBody@YGNode_baseline_with_baseline_fn_Test@@EEAAXXZ)
7>  YGNodeCallbackTest.obj : error LNK2019: unresolved external symbol "public: void __cdecl YGNode::setMeasureFunc(struct YGSize (__cdecl*)(struct YGNode *,float,enum YGMeasureMode,float,enum YGMeasureMode,void *))" (?setMeasureFunc@YGNode@@QEAAXP6A?AUYGSize@@PEAU1@MW4YGMeasureMode@@M1PEAX@Z@Z) referenced in function "private: virtual void __cdecl YGNode_measure_with_context_measure_fn_Test::TestBody(void)" (?TestBody@YGNode_measure_with_context_measure_fn_Test@@EEAAXXZ)
7>  YGConfigTest.obj : error LNK2019: unresolved external symbol "public: void __cdecl YGConfig::setCloneNodeCallback(struct YGNode * (__cdecl*)(struct YGNode *,struct YGNode *,int))" (?setCloneNodeCallback@YGConfig@@QEAAXP6APEAUYGNode@@PEAU2@0H@Z@Z) referenced in function "private: virtual void __cdecl ConfigCloningTest_uses_values_provided_by_cloning_callback_Test::TestBody(void)" (?TestBody@ConfigCloningTest_uses_values_provided_by_cloning_callback_Test@@EEAAXXZ)
7>  YGConfigTest.obj : error LNK2019: unresolved external symbol "public: void __cdecl YGConfig::setCloneNodeCallback(struct YGNode * (__cdecl*)(struct YGNode *,struct YGNode *,int,void *))" (?setCloneNodeCallback@YGConfig@@QEAAXP6APEAUYGNode@@PEAU2@0HPEAX@Z@Z) referenced in function "private: virtual void __cdecl ConfigCloningTest_can_clone_with_context_Test::TestBody(void)" (?TestBody@ConfigCloningTest_can_clone_with_context_Test@@EEAAXXZ)
7>  YGConfigTest.obj : error LNK2019: unresolved external symbol "public: struct YGNode * __cdecl YGConfig::cloneNode(struct YGNode *,struct YGNode *,int,void *)const " (?cloneNode@YGConfig@@QEBAPEAUYGNode@@PEAU2@0HPEAX@Z) referenced in function "private: virtual void __cdecl ConfigCloningTest_uses_values_provided_by_cloning_callback_Test::TestBody(void)" (?TestBody@ConfigCloningTest_uses_values_provided_by_cloning_callback_Test@@EEAAXXZ)
7>  TestUtil.obj : error LNK2019: unresolved external symbol "public: static void __cdecl facebook::yoga::Event::reset(void)" (?reset@Event@yoga@facebook@@SAXXZ) referenced in function "public: static int __cdecl facebook::yoga::test::TestUtil::stopCountingNodes(void)" (?stopCountingNodes@TestUtil@test@yoga@facebook@@SAHXZ)
7>  TestUtil.obj : error LNK2019: unresolved external symbol "public: static void __cdecl facebook::yoga::Event::subscribe(class std::function<void __cdecl(struct YGNode const &,enum facebook::yoga::Event::Type,class facebook::yoga::Event::Data)> &&)" (?subscribe@Event@yoga@facebook@@SAX$$QEAV?$function@$$A6AXAEBUYGNode@@W4Type@Event@yoga@facebook@@VData@345@@Z@std@@@Z) referenced in function "public: static void __cdecl facebook::yoga::test::TestUtil::startCountingNodes(void)" (?startCountingNodes@TestUtil@test@yoga@facebook@@SAXXZ)
7>  C:\Users\steve\Desktop\SquidFX\yoga\build\tests\Debug\yogatests.exe : fatal error LNK1120: 16 unresolved externals
7>Done building target "Link" in project "yogatests.vcxproj" -- FAILED.
7>
7>Done building project "yogatests.vcxproj" -- FAILED.
7>
7>Build FAILED.
7>
7>CompactValueTest.obj : error LNK2001: unresolved external symbol YGValueAuto
7>CompactValueTest.obj : error LNK2001: unresolved external symbol YGValueUndefined
7>EventsTest.obj : error LNK2019: unresolved external symbol YGNodeSetBaselineFunc referenced in function "private: virtual void __cdecl facebook::yoga::test::EventTest_baseline_functions_get_wrapped_Test::TestBody(void)" (?TestBody@EventTest_baseline_functions_get_wrapped_Test@test@yoga@facebook@@EEAAXXZ)
7>YGAlignBaselineTest.obj : error LNK2001: unresolved external symbol YGNodeSetBaselineFunc
7>YGNodeCallbackTest.obj : error LNK2001: unresolved external symbol "public: void __cdecl YGNode::setMeasureFunc(struct YGSize (__cdecl*)(struct YGNode *,float,enum YGMeasureMode,float,enum YGMeasureMode))" (?setMeasureFunc@YGNode@@QEAAXP6A?AUYGSize@@PEAU1@MW4YGMeasureMode@@M1@Z@Z)
7>YGRoundingMeasureFuncTest.obj : error LNK2001: unresolved external symbol "public: void __cdecl YGNode::setMeasureFunc(struct YGSize (__cdecl*)(struct YGNode *,float,enum YGMeasureMode,float,enum YGMeasureMode))" (?setMeasureFunc@YGNode@@QEAAXP6A?AUYGSize@@PEAU1@MW4YGMeasureMode@@M1@Z@Z)
7>YGAspectRatioTest.obj : error LNK2001: unresolved external symbol "public: void __cdecl YGNode::setMeasureFunc(struct YGSize (__cdecl*)(struct YGNode *,float,enum YGMeasureMode,float,enum YGMeasureMode))" (?setMeasureFunc@YGNode@@QEAAXP6A?AUYGSize@@PEAU1@MW4YGMeasureMode@@M1@Z@Z)
7>YGMeasureCacheTest.obj : error LNK2001: unresolved external symbol "public: void __cdecl YGNode::setMeasureFunc(struct YGSize (__cdecl*)(struct YGNode *,float,enum YGMeasureMode,float,enum YGMeasureMode))" (?setMeasureFunc@YGNode@@QEAAXP6A?AUYGSize@@PEAU1@MW4YGMeasureMode@@M1@Z@Z)
7>YGMeasureModeTest.obj : error LNK2001: unresolved external symbol "public: void __cdecl YGNode::setMeasureFunc(struct YGSize (__cdecl*)(struct YGNode *,float,enum YGMeasureMode,float,enum YGMeasureMode))" (?setMeasureFunc@YGNode@@QEAAXP6A?AUYGSize@@PEAU1@MW4YGMeasureMode@@M1@Z@Z)
7>YGMeasureTest.obj : error LNK2001: unresolved external symbol "public: void __cdecl YGNode::setMeasureFunc(struct YGSize (__cdecl*)(struct YGNode *,float,enum YGMeasureMode,float,enum YGMeasureMode))" (?setMeasureFunc@YGNode@@QEAAXP6A?AUYGSize@@PEAU1@MW4YGMeasureMode@@M1@Z@Z)
7>YGDirtiedTest.obj : error LNK2019: unresolved external symbol "public: void __cdecl YGNode::setDirty(bool)" (?setDirty@YGNode@@QEAAX_N@Z) referenced in function "private: virtual void __cdecl YogaTest_dirtied_Test::TestBody(void)" (?TestBody@YogaTest_dirtied_Test@@EEAAXXZ)
7>YGDirtiedTest.obj : error LNK2019: unresolved external symbol "public: void __cdecl YGNode::markDirtyAndPropagate(void)" (?markDirtyAndPropagate@YGNode@@QEAAXXZ) referenced in function "private: virtual void __cdecl YogaTest_dirtied_propagation_Test::TestBody(void)" (?TestBody@YogaTest_dirtied_propagation_Test@@EEAAXXZ)
7>YGLoggerTest.obj : error LNK2019: unresolved external symbol YGConfigSetPrintTreeFlag referenced in function "private: virtual void __cdecl YogaTest_config_print_tree_enabled_Test::TestBody(void)" (?TestBody@YogaTest_config_print_tree_enabled_Test@@EEAAXXZ)
7>YGNodeCallbackTest.obj : error LNK2019: unresolved external symbol "public: __cdecl YGNode::YGNode(struct YGConfig * const)" (??0YGNode@@QEAA@QEAUYGConfig@@@Z) referenced in function "public: __cdecl YGNode::YGNode(void)" (??0YGNode@@QEAA@XZ)
7>YGConfigTest.obj : error LNK2001: unresolved external symbol "public: __cdecl YGNode::YGNode(struct YGConfig * const)" (??0YGNode@@QEAA@QEAUYGConfig@@@Z)
7>YGNodeCallbackTest.obj : error LNK2019: unresolved external symbol "public: struct YGSize __cdecl YGNode::measure(float,enum YGMeasureMode,float,enum YGMeasureMode,void *)" (?measure@YGNode@@QEAA?AUYGSize@@MW4YGMeasureMode@@M0PEAX@Z) referenced in function "private: virtual void __cdecl YGNode_measure_with_measure_fn_Test::TestBody(void)" (?TestBody@YGNode_measure_with_measure_fn_Test@@EEAAXXZ)
7>YGNodeCallbackTest.obj : error LNK2019: unresolved external symbol "public: float __cdecl YGNode::baseline(float,float,void *)" (?baseline@YGNode@@QEAAMMMPEAX@Z) referenced in function "private: virtual void __cdecl YGNode_baseline_with_baseline_fn_Test::TestBody(void)" (?TestBody@YGNode_baseline_with_baseline_fn_Test@@EEAAXXZ)
7>YGNodeCallbackTest.obj : error LNK2019: unresolved external symbol "public: void __cdecl YGNode::setMeasureFunc(struct YGSize (__cdecl*)(struct YGNode *,float,enum YGMeasureMode,float,enum YGMeasureMode,void *))" (?setMeasureFunc@YGNode@@QEAAXP6A?AUYGSize@@PEAU1@MW4YGMeasureMode@@M1PEAX@Z@Z) referenced in function "private: virtual void __cdecl YGNode_measure_with_context_measure_fn_Test::TestBody(void)" (?TestBody@YGNode_measure_with_context_measure_fn_Test@@EEAAXXZ)
7>YGConfigTest.obj : error LNK2019: unresolved external symbol "public: void __cdecl YGConfig::setCloneNodeCallback(struct YGNode * (__cdecl*)(struct YGNode *,struct YGNode *,int))" (?setCloneNodeCallback@YGConfig@@QEAAXP6APEAUYGNode@@PEAU2@0H@Z@Z) referenced in function "private: virtual void __cdecl ConfigCloningTest_uses_values_provided_by_cloning_callback_Test::TestBody(void)" (?TestBody@ConfigCloningTest_uses_values_provided_by_cloning_callback_Test@@EEAAXXZ)
7>YGConfigTest.obj : error LNK2019: unresolved external symbol "public: void __cdecl YGConfig::setCloneNodeCallback(struct YGNode * (__cdecl*)(struct YGNode *,struct YGNode *,int,void *))" (?setCloneNodeCallback@YGConfig@@QEAAXP6APEAUYGNode@@PEAU2@0HPEAX@Z@Z) referenced in function "private: virtual void __cdecl ConfigCloningTest_can_clone_with_context_Test::TestBody(void)" (?TestBody@ConfigCloningTest_can_clone_with_context_Test@@EEAAXXZ)
7>YGConfigTest.obj : error LNK2019: unresolved external symbol "public: struct YGNode * __cdecl YGConfig::cloneNode(struct YGNode *,struct YGNode *,int,void *)const " (?cloneNode@YGConfig@@QEBAPEAUYGNode@@PEAU2@0HPEAX@Z) referenced in function "private: virtual void __cdecl ConfigCloningTest_uses_values_provided_by_cloning_callback_Test::TestBody(void)" (?TestBody@ConfigCloningTest_uses_values_provided_by_cloning_callback_Test@@EEAAXXZ)
7>TestUtil.obj : error LNK2019: unresolved external symbol "public: static void __cdecl facebook::yoga::Event::reset(void)" (?reset@Event@yoga@facebook@@SAXXZ) referenced in function "public: static int __cdecl facebook::yoga::test::TestUtil::stopCountingNodes(void)" (?stopCountingNodes@TestUtil@test@yoga@facebook@@SAHXZ)
7>TestUtil.obj : error LNK2019: unresolved external symbol "public: static void __cdecl facebook::yoga::Event::subscribe(class std::function<void __cdecl(struct YGNode const &,enum facebook::yoga::Event::Type,class facebook::yoga::Event::Data)> &&)" (?subscribe@Event@yoga@facebook@@SAX$$QEAV?$function@$$A6AXAEBUYGNode@@W4Type@Event@yoga@facebook@@VData@345@@Z@std@@@Z) referenced in function "public: static void __cdecl facebook::yoga::test::TestUtil::startCountingNodes(void)" (?startCountingNodes@TestUtil@test@yoga@facebook@@SAXXZ)
7>C:\Users\steve\Desktop\SquidFX\yoga\build\tests\Debug\yogatests.exe : fatal error LNK1120: 16 unresolved externals
7>    0 Warning(s)
7>    24 Error(s)
7>
7>Time Elapsed 00:00:50.54
8>------ Build started: Project: ALL_BUILD, Configuration: Debug x64 ------
8>Build started 8/8/2023 6:21:45 PM.
8>Target ResolveProjectReferences:
8>  Target GetTargetPathWithTargetPlatformMoniker:
8>Target PrepareForBuild:
8>  Creating directory "x64\Debug\ALL_BUILD\ALL_BUILD.tlog\".
8>Target InitializeBuildStatus:
8>  Creating "x64\Debug\ALL_BUILD\ALL_BUILD.tlog\unsuccessfulbuild" because "AlwaysCreate" was specified.
8>Target CustomBuild:
8>  Building Custom Rule C:/Users/steve/Desktop/SquidFX/yoga/CMakeLists.txt
8>Target GetReferencedVCProjectsInfo:
8>  Target DisableProjectReferenceRecursionForClInputs:
8>Target FinalizeBuildStatus:
8>  Deleting file "x64\Debug\ALL_BUILD\ALL_BUILD.tlog\unsuccessfulbuild".
8>  Touching "x64\Debug\ALL_BUILD\ALL_BUILD.tlog\ALL_BUILD.lastbuildstate".
8>
8>Build succeeded.
8>    0 Warning(s)
8>    0 Error(s)
8>
8>Time Elapsed 00:00:00.16
9>------ Skipped Build: Project: INSTALL, Configuration: Debug x64 ------
9>Project not selected to build for this solution configuration 
========== Build: 7 succeeded, 1 failed, 0 up-to-date, 1 skipped ==========
========== Build started at 6:20 PM and took 57.630 seconds ==========

@Nitikonkon2534
Copy link

I need to build Yoga as a shared library so that I can create C# bindings using p/invoke. Below is the updated CMakeLists.txt file for the 'yoga' directory. It includes an option to build Yoga as a shared library. Please note that although it successfully builds Yoga as a working shared library, it still throws an error. I suspect the issue is with yogatest trying to build against the Yoga static library. Any help with working on this would be greatly appreciated.

Error

collect2: error: ld returned 1 exit status
make[2]: *** [tests/CMakeFiles/yogatests.dir/build.make:887: tests/yogatests] Error 1
make[2]: Leaving directory '/home/sstarr/Downloads/yoga-2.0.0/build'
make[1]: *** [CMakeFiles/Makefile2:199: tests/CMakeFiles/yogatests.dir/all] Error 2
make[1]: Leaving directory '/home/sstarr/Downloads/yoga-2.0.0/build'
make: *** [Makefile:139: all] Error 2

yoga/CMakeLists.txt

# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.


cmake_minimum_required(VERSION 3.13...3.26)
project(yogacore)
set(CMAKE_VERBOSE_MAKEFILE on)

if(TARGET yogacore)
    return()
endif()

include(CheckIPOSupported)

# Define an option to choose between shared and static build
option(BUILD_SHARED_LIBS "Build yogacore as a shared library" OFF)

set(YOGA_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/..)
include(${YOGA_ROOT}/cmake/project-defaults.cmake)

file(GLOB SOURCES CONFIGURE_DEPENDS
    ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp
    ${CMAKE_CURRENT_SOURCE_DIR}/**/*.cpp)

# Choose between adding a shared or static library based on the BUILD_SHARED_LIBS option
if(BUILD_SHARED_LIBS)
    add_library(yogacore SHARED ${SOURCES})
else()
    add_library(yogacore STATIC ${SOURCES})
endif()

# Yoga conditionally uses <android/log> when building for Android
if (ANDROID)
    target_link_libraries(yogacore log)
endif()

check_ipo_supported(RESULT result)
if(result)
    set_target_properties(yogacore PROPERTIES
        CMAKE_INTERPROCEDURAL_OPTIMIZATION true)
endif()

target_include_directories(yogacore
    PUBLIC
    $<BUILD_INTERFACE:${YOGA_ROOT}>
    $<INSTALL_INTERFACE:${CMAKE_INSTALL_PREFIX}/include/yoga>)

@Nitikonkon2534
Copy link

#1341 (comment)

@wtopolski
Copy link

Hi @NickGerleman,

Disclaimer: I am (almost) absolutely a beginner in the JNI/cmake/C++, every action described below was an act of monkey typing, cope-paste and googling.

My idea is to execute java unit tests with Yoga as a native library. I aim to create a JAR file with an embedded binary that is compatible with Linux. I've attempted to achieve this by running commands from workflow, on a virtual machine running Ubuntu. (It's important to note that I've installed necessary tools such as Ninja, CMake, etc., and the unit tests and benchmarks execute correctly.)

For building the binary, I utilized the CMakeLists.txt from the Java directory. I made modifications (basing on v2.0.1) to the CMakeLists.txt to exclude Android NDK stuff (liblog.so - minor issue because android-liblog package, libm.so - not found workaround for it) . I have added find_package(JNI REQUIRED), include_directories(${JNI_INCLUDE_DIRS}) and turn off -Werror (I should not)

I based on v2.0.1, repo was cloned to dir namedfacebook

▶ git diff
diff --git a/cmake/project-defaults.cmake b/cmake/project-defaults.cmake
index b6b37bcc..0939b779 100644
--- a/cmake/project-defaults.cmake
+++ b/cmake/project-defaults.cmake
@@ -34,7 +34,7 @@ add_compile_options(
     # Enable warnings and warnings as errors
     -Wall
     -Wextra
-    -Werror
+    #-Werror
     # Disable RTTI
     $<$<COMPILE_LANGUAGE:CXX>:-fno-rtti>
     # Use -O2 (prioritize speed)
diff --git a/java/CMakeLists.txt b/java/CMakeLists.txt
index d8afd671..adfea0fd 100644
--- a/java/CMakeLists.txt
+++ b/java/CMakeLists.txt
@@ -4,7 +4,12 @@
 # LICENSE file in the root directory of this source tree.
 
 cmake_minimum_required(VERSION 3.13...3.26)
 project(yogajni)
+find_package(JNI REQUIRED)
+include_directories(${JNI_INCLUDE_DIRS})
 set(CMAKE_VERBOSE_MAKEFILE on)
 
 set(YOGA_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/..)

Next I run cmake commands:

root@ubuntu-yoga:~/facebook/java# cmake -S . -B build -G Ninja -D CMAKE_BUILD_TYPE=Release
-- The C compiler identification is GNU 11.4.0
-- The CXX compiler identification is GNU 11.4.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Found JNI: /usr/lib/jvm/java-17-openjdk-amd64/lib/libjawt.so  
-- Configuring done
-- Generating done
-- Build files have been written to: /root/facebook/java/build

getting some warning (more than I paste here, but all of them looks similar):

root@ubuntu-yoga:~/facebook/java# cmake --build build
/root/facebook/java/jni/YGJNIVanilla.cpp:100: warning: ignoring ‘#pragma clang diagnostic’ [-Wunknown-pragmas]
  100 | #pragma clang diagnostic pop
      | 
/root/facebook/java/jni/YGJNIVanilla.cpp: In function ‘void jni_YGConfigSetUseLegacyStretchBehaviourJNI(JNIEnv*, jobject, jlong, jboolean)’:
/root/facebook/java/jni/YGJNIVanilla.cpp:99:39: warning: ‘void YGConfigSetUseLegacyStretchBehaviour(YGConfigRef, bool)’ is deprecated: "YGConfigSetUseLegacyStretchBehaviour" will be removed in the next release. Usage should be replaced with "YGConfigSetErrata(YGErrataAll)" to opt out of all future breaking conformance fixes, or "YGConfigSetErrata(YGErrataStretchFlexBasis)" to opt out of the specific conformance fix previously disabled by "UseLegacyStretchBehaviour". [-Wdeprecated-declarations]
   99 |   YGConfigSetUseLegacyStretchBehaviour(config, useLegacyStretchBehaviour);
      |   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from /root/facebook/java/jni/YGJNI.h:8,
                 from /root/facebook/java/jni/YGJNIVanilla.cpp:11:
/root/facebook/yoga/../yoga/Yoga.h:339:6: note: declared here
  339 | void YGConfigSetUseLegacyStretchBehaviour(
      |      ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/root/facebook/java/jni/YGJNIVanilla.cpp: At global scope:
/root/facebook/java/jni/YGJNIVanilla.cpp:775:6: warning: ISO C++ forbids converting a string constant to ‘char*’ [-Wwrite-strings]
/root/facebook/java/jni/YGJNIVanilla.cpp:994:6: warning: ISO C++ forbids converting a string constant to ‘char*’ [-Wwrite-strings]
  994 |     {"jni_YGNodeStyleSetMaxHeightJNI",
      |      ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/root/facebook/java/jni/YGJNIVanilla.cpp:995:6: warning: ISO C++ forbids converting a string constant to ‘char*’ [-Wwrite-strings]
  995 |      "(JF)V",
      |      ^~~~~~~
/root/facebook/java/jni/YGJNIVanilla.cpp:1016:1: warning: ISO C++ forbids converting a string constant to ‘char*’ [-Wwrite-strings]
[18/18] Linking CXX shared library libyoga.so

But I got my libyoga.so (smaller than expected, around 150KB, android version was ~500KB), because when I run simple code like this:

public class Main {
   public static void main(String[] args) {
       System.out.println("Before");
       System.load("/root/libyoga.so");
       System.out.println("After");
   }
}

App blows up (surprise):

root@ubuntu-yoga:~# java -jar yogarun-1.0-SNAPSHOT.jar
Before
terminate called after throwing an instance of 'facebook::yoga::vanillajni::YogaJniException'
  what():  std::exception
Aborted (core dumped)

So, I must do something wrong, that's for sure. Maybe there is something extra done by android NDK? Maybe C++ standard is wrong? Please give me some clue.

Ps. I also tried the same steps on macos where I got different errors :/

@wtopolski
Copy link

wtopolski commented Dec 13, 2023

Small update. I have find out that my jar should contain java sources, because facebook::yoga::vanillajni::registerNatives looks for them. When I added whole com.facebook.... package, it started working (yupi).

I still have those scary warnings on linux (and compile errors on mac), so if you have any advice about env configuration please tell.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants