Repository: conan-io/conan-center-index

Issue:
[package] libx265/all: static libx265 cannot be used in shared libraries (on unix systems)
The statically built libx265 conan package cannot be used in a shared library because the assembly files are not PIC compatible.
libx265 provides a [`ENABLE_ASSEMBLY`](https://github.com/videolan/x265/blob/241342f25bd1a83678b24588712f91ca0bff99f3/source/CMakeLists.txt#L352-L365) option to conditionally enable/disable the assembly.

The libx265 recipe should get a new option (`assembly`) that sets the cmake define `ENABLE_ASSEMBLY`.
And when this option `assembly` is enabled: -o shared=False -o fPIC=True and -o shared=True should be disallowed.
WDYT?

See https://github.com/conan-io/conan-center-index/pull/3412#issuecomment-723205661 for more analysis.


### Package and Environment Details (include every applicable attribute)
  * Package Name/Version: **libx265/all**
  * Operating System+version: **Linux** + **Apple**
  * Compiler+version: **GCC 9**
  * Conan version: **conan 1.31.1**
  * Python version: **Python 3.7.7**

(problem detected on gcc9@Linux)


Discussion:

Resolving patch:
diff --git a/recipes/libx265/all/conanfile.py b/recipes/libx265/all/conanfile.py
--- a/recipes/libx265/all/conanfile.py
+++ b/recipes/libx265/all/conanfile.py
@@ -16,6 +16,7 @@ class Libx265Conan(ConanFile):
     options = {
         "shared": [True, False],
         "fPIC": [True, False],
+        "assembly": [True, False],
         "bit_depth": [8, 10, 12],
         "HDR10": [True, False],
         "SVG_HEVC_encoder": [True, False],
@@ -23,6 +24,7 @@ class Libx265Conan(ConanFile):
     default_options = {
         "shared": False,
         "fPIC": True,
+        "assembly": True,
         "bit_depth": 8,
         "HDR10": False,
         "SVG_HEVC_encoder": False,
@@ -46,6 +48,10 @@ def configure(self):
         if self.options.shared:
             del self.options.fPIC
 
+    def build_requirements(self):
+        if self.options.assembly:
+            self.build_requires("nasm/2.14")
+
     def source(self):
         tools.get(**self.conan_data["sources"][self.version])
         os.rename("x265-{}".format(self.version), self._source_subfolder)
@@ -54,7 +60,9 @@ def _configure_cmake(self):
         if self._cmake:
             return self._cmake
         self._cmake = CMake(self)
+        self._cmake.definitions["ENABLE_PIC"] = self.options.get_safe("fPIC", True)
         self._cmake.definitions["ENABLE_SHARED"] = self.options.shared
+        self._cmake.definitions["ENABLE_ASSEMBLY"] = self.options.assembly
         self._cmake.definitions["ENABLE_LIBNUMA"] = False
         if self.settings.os == "Macos":
             self._cmake.definitions["CMAKE_SHARED_LINKER_FLAGS"] = "-Wl,-read_only_relocs,suppress"
@@ -115,14 +123,16 @@ def package(self):
     def package_info(self):
         self.cpp_info.names["pkg_config"] = "x265"
         self.cpp_info.libs = ["x265"]
-        if self.settings.os == "Linux":
+        if self.settings.os == "Windows":
+            if self.options.shared:
+                self.cpp_info.defines.append("X265_API_IMPORTS")
+        elif self.settings.os == "Linux":
             self.cpp_info.system_libs.extend(["dl", "pthread", "m"])
-        if self.settings.os == "Android":
+            if not self.options.shared:
+                if self.settings.compiler != "Visual Studio":
+                    self.cpp_info.sharedlinkflags = ["-Wl,-Bsymbolic,-znoexecstack"]
+        elif self.settings.os == "Android":
             self.cpp_info.libs.extend(["dl", "m"])
-        libcxx = self.settings.get_safe("compiler.libcxx")
-        if libcxx in ["libstdc++", "libstdc++11"]:
-            self.cpp_info.system_libs.append("stdc++")
-        elif libcxx == "libc++":
-            self.cpp_info.system_libs.append("c++")
-        elif libcxx in ["c++_static", "c++_shared"]:
-            self.cpp_info.system_libs.extend([libcxx, "c++abi"])
+        libcxx = tools.stdcpp_library(self)
+        if libcxx:
+            self.cpp_info.system_libs.append(libcxx)


Test patch:
diff --git a/recipes/libx265/all/test_package/CMakeLists.txt b/recipes/libx265/all/test_package/CMakeLists.txt
--- a/recipes/libx265/all/test_package/CMakeLists.txt
+++ b/recipes/libx265/all/test_package/CMakeLists.txt
@@ -4,5 +4,16 @@ project(test_package)
 include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
 conan_basic_setup()
 
-add_executable(${PROJECT_NAME} test_package.cpp)
+add_executable(${PROJECT_NAME} test_package.c)
 target_link_libraries(${PROJECT_NAME} ${CONAN_LIBS})
+
+option(TEST_LIBRARY "Build test library")
+if(TEST_LIBRARY)
+    # Test building a shared library.
+    # a static libx265.a on Linux requires linker flags to avoid PIC related relocation errors.
+    add_library(test_library SHARED test_library.c)
+    target_link_libraries(test_library ${CONAN_LIBS})
+
+    target_compile_definitions(${PROJECT_NAME} PRIVATE WITH_LIB)
+    target_link_libraries(${PROJECT_NAME} test_library)
+endif()
diff --git a/recipes/libx265/all/test_package/conanfile.py b/recipes/libx265/all/test_package/conanfile.py
--- a/recipes/libx265/all/test_package/conanfile.py
+++ b/recipes/libx265/all/test_package/conanfile.py
@@ -1,17 +1,32 @@
 from conans import ConanFile, CMake, tools
+from conans.errors import ConanException
 import os
+import glob
+import shutil
 
 
 class TestPackageConan(ConanFile):
     settings = "os", "compiler", "build_type", "arch"
     generators = "cmake"
 
+    @property
+    def _test_shared_library(self):
+        try:
+            return self.options["libx265"].fPIC
+        except ConanException:
+            return True
+
     def build(self):
         cmake = CMake(self)
+        cmake.definitions["TEST_LIBRARY"] = self._test_shared_library
         cmake.configure()
         cmake.build()
 
     def test(self):
+        # Copy all libraries to current and bin folderso we don't need any LD_LIBRARY_PATH/DYLD_LIBRARY_PATH/PATH (which might conflict with run_environment argument of self.run)
+        for fn in glob.glob(os.path.join("lib", "*")):
+            shutil.copy(src=fn, dst="bin")
+            shutil.copy(src=fn, dst=".")
         if not tools.cross_building(self.settings):
             bin_path = os.path.join("bin", "test_package")
             self.run(bin_path, run_environment=True)
diff --git a/recipes/libx265/all/test_package/test_package.cpp b/recipes/libx265/all/test_package/test_library.c
similarity index 53%
rename from recipes/libx265/all/test_package/test_package.cpp
rename to recipes/libx265/all/test_package/test_library.c
--- a/recipes/libx265/all/test_package/test_package.cpp
+++ b/recipes/libx265/all/test_package/test_library.c
@@ -1,9 +1,18 @@
 #include "x265.h"
 
-#include <cstdlib>
+#include <stdlib.h>
 
-int main()
-{
+#ifdef _WIN32
+# define TEST_EXPORT __declspec(dllexport)
+#else
+# define TEST_EXPORT __attribute__ ((visibility("default")))
+#endif
+
+TEST_EXPORT const char *test_library_version(void) {
+    return x265_version_str;
+}
+
+TEST_EXPORT void test_library_something(void) {
     x265_param * param = x265_param_alloc();
     x265_param_default(param);
     param->sourceWidth = 640;
@@ -13,5 +22,4 @@ int main()
     x265_encoder * encoder = x265_encoder_open(param);
     x265_encoder_close(encoder);
     x265_param_free(param);
-    return EXIT_SUCCESS;
 }
diff --git a/recipes/libx265/all/test_package/test_package.c b/recipes/libx265/all/test_package/test_package.c
new file mode 100644
--- /dev/null
+++ b/recipes/libx265/all/test_package/test_package.c
@@ -0,0 +1,32 @@
+#include "x265.h"
+
+#include <stdlib.h>
+
+#ifdef WITH_LIB
+# ifdef _WIN32
+#  define TEST_IMPORT __declspec(dllimport)
+# else
+#  define TEST_IMPORT
+# endif
+
+TEST_IMPORT const char *test_library_version(void);
+TEST_IMPORT void test_library_something(void);
+#endif
+
+int main()
+{
+    x265_param * param = x265_param_alloc();
+    x265_param_default(param);
+    param->sourceWidth = 640;
+    param->sourceHeight = 480;
+    param->fpsNum = 25;
+    param->fpsDenom = 1;
+    x265_encoder * encoder = x265_encoder_open(param);
+    x265_encoder_close(encoder);
+    x265_param_free(param);
+#ifdef WITH_LIB
+    test_library_version();
+    test_library_something();
+#endif
+    return EXIT_SUCCESS;
+}