conan-io / conan

Conan - The open-source C and C++ package manager
https://conan.io
MIT License
8.32k stars 986 forks source link

[question] components linking error #17394

Open JamesPei opened 5 days ago

JamesPei commented 5 days ago

help! I'm using conan2 to create FFmpeg package, and this is package_info:

class ffmpegRecipe(ConanFile):
    name = "ffmpeg"
    version = "7.1"
    package_type = "application"

    url = "https://github.com/FFmpeg/FFmpeg/tree/master?tab=readme-ov-file"
    topics = ("tools", "heavy", "video", "multimedia", "codec")
    settings = "os", "arch", "compiler", "build_type"

    options = {
        "shared": [True, False],
        "with_x264": [True, False],
        "with_x265": [True, False],
        "with_GPU": [True, False],
    }
    default_options = {
        "shared": True,
        "with_x264": True,
        "with_x265": True,
        "with_GPU": False
    }

    def source(self):
        local_source_dir = os.environ.get("LOCAL_SOURCE_DIR", None)
        if local_source_dir and os.path.exists(local_source_dir):
            self.output.info(f"Using local source directory: {local_source_dir}")
            copy(self, pattern="*", src=local_source_dir, dst=self.source_folder)
        else:
            self.output.info("Cloning FFmpeg repository...")
            git = Git(self)
            git.clone(url="git@github.com:FFmpeg/FFmpeg.git", target=".")

    def config_options(self):
        if self.settings.os == "Windows":
            self.options.rm_safe("fPIC")

    def configure(self):
        if self.options.shared:
            self.options.rm_safe("fPIC")

    def build(self):
        configure_command = "./configure --prefix={} \
            --enable-gpl \
            --enable-libx264 \
            --enable-libx265".format(self.build_folder)

        if self.options.shared:
            configure_command+=" --enable-shared"

        if self.options.with_GPU:
            configure_command+=" --enable-nonfree --enable-cuda-nvcc --enable-libnpp"

        self.run(configure_command)
        self.run("make -j8")

    def package(self):
        copy(self, pattern="ffmpeg", dst=os.path.join(self.package_folder, "bin"), src=self.build_folder)
        copy(self, pattern="ffprobe", dst=os.path.join(self.package_folder, "bin"), src=self.build_folder)

        copy(self, pattern="*.h",   dst=os.path.join(self.package_folder,"include/libavcodec"),     src=os.path.join(self.build_folder,"libavcodec"))
        copy(self, pattern="*.so*", dst=os.path.join(self.package_folder,"lib"),                    src=os.path.join(self.build_folder,"libavcodec"))
        copy(self, pattern="*.h",   dst=os.path.join(self.package_folder,"include/libavdevice"),    src=os.path.join(self.build_folder,"libavdevice"))
        copy(self, pattern="*.so*", dst=os.path.join(self.package_folder,"lib"),                    src=os.path.join(self.build_folder,"libavdevice"))
        copy(self, pattern="*.h",   dst=os.path.join(self.package_folder,"include/libavfilter"),    src=os.path.join(self.build_folder,"libavfilter"))  
        copy(self, pattern="*.so*", dst=os.path.join(self.package_folder,"lib"),                    src=os.path.join(self.build_folder,"libavfilter"))
        copy(self, pattern="*.h",   dst=os.path.join(self.package_folder,"include/libavformat"),    src=os.path.join(self.build_folder,"libavformat"))  
        copy(self, pattern="*.so*", dst=os.path.join(self.package_folder,"lib"),                    src=os.path.join(self.build_folder,"libavformat"))
        copy(self, pattern="*.h",   dst=os.path.join(self.package_folder,"include/libavutil"),      src=os.path.join(self.build_folder,"libavutil"))    
        copy(self, pattern="*.so*", dst=os.path.join(self.package_folder,"lib"),                    src=os.path.join(self.build_folder,"libavutil"))
        copy(self, pattern="*.h",   dst=os.path.join(self.package_folder,"include/libpostproc"),    src=os.path.join(self.build_folder,"libpostproc"))  
        copy(self, pattern="*.so*", dst=os.path.join(self.package_folder,"lib"),                    src=os.path.join(self.build_folder,"libpostproc"))
        copy(self, pattern="*.h",   dst=os.path.join(self.package_folder,"include/libswresample"),  src=os.path.join(self.build_folder,"libswresample"))
        copy(self, pattern="*.so*", dst=os.path.join(self.package_folder,"lib"),                    src=os.path.join(self.build_folder,"libswresample"))
        copy(self, pattern="*.h",   dst=os.path.join(self.package_folder,"include/libswscale"),     src=os.path.join(self.build_folder,"libswscale"))
        copy(self, pattern="*.so*", dst=os.path.join(self.package_folder,"lib"),                    src=os.path.join(self.build_folder,"libswscale"))

    def package_info(self):
        self.cpp_info.components["avutil"].libs = ["avutil"]
        self.cpp_info.components["avutil"].set_property("cmake_target_name", "avutil")

        self.cpp_info.components["avcodec"].libs = ["avcodec"]
        self.cpp_info.components["avcodec"].requires = ["avutil"]
        self.cpp_info.components["avcodec"].set_property("cmake_target_name", "avcodec")

        self.cpp_info.components["avformat"].set_property("cmake_target_name", "avformat")
        self.cpp_info.components["avformat"].libs = ["avforamt"]
        self.cpp_info.components["avformat"].requires = ["avcodec", "avutil"]

        self.cpp_info.components["swscale"].libs = ["swscale"]
        self.cpp_info.components["swscale"].requires = ["avutil"]
        self.cpp_info.components["swscale"].set_property("cmake_target_name", "swscale")

        self.cpp_info.components["swresample"].libs = ["swresample"]
        self.cpp_info.components["swresample"].requires = ["avutil"]
        self.cpp_info.components["swresample"].set_property("cmake_target_name", "swresample")

        self.cpp_info.components["postproc"].libs = ["postproc"]
        self.cpp_info.components["postproc"].requires = ["avutil"]
        self.cpp_info.components["postproc"].set_property("cmake_target_name", "postproc")

        self.cpp_info.components["avfilter"].libs = ["avfilter"]
        self.cpp_info.components["avfilter"].requires = ["avutil", "swscale", "postproc", "avformat", "avcodec", "swresample"]
        self.cpp_info.components["avfilter"].set_property("cmake_target_name", "avfilter")

        self.cpp_info.components["avdevice"].libs = ["avdevice"]
        self.cpp_info.components["avdevice"].requires = ["avutil", "avformat", "avcodec", "avfilter"]
        self.cpp_info.components["avdevice"].set_property("cmake_target_name", "avdevice")

and then I tried to use it to build another project,here is some information:

def requirements(self):
        self.requires("ffmpeg/[~7.1]")
        self.requires("cxxopts/[~3.2]")
        self.requires("nlohmann_json/[~3.11]")

def system_requirements(self):
        Apt(self).install(["libvdpau-dev", "libx11-dev", "libva-dev"])

def build_requirements(self):
        self.tool_requires("cmake/[~3.22]")

def layout(self):
        cmake_layout(self)

def generate(self):
        deps = CMakeDeps(self)
        deps.check_components_exist = True
        deps.generate()
        tc = CMakeToolchain(self)
        tc.generate()

def build(self):
        cmake = CMake(self)
        cmake.configure()
        cmake.build()

def package(self):
        cmake = CMake(self)
        cmake.install()

it reports undefined reference to xxx' error, like undefined reference toavcodec_open2'; so I check generated cmake by CMakeDeps generator, and in ffmpeg-release-x86_64-data.cmake I found all xxx_LIBS_RELEASE is not setted, for example

set(ffmpeg_avutil_INCLUDE_DIRS_RELEASE )
set(ffmpeg_avutil_LIB_DIRS_RELEASE "${ffmpeg_PACKAGE_FOLDER_RELEASE}/lib")
set(ffmpeg_avutil_BIN_DIRS_RELEASE "${ffmpeg_PACKAGE_FOLDER_RELEASE}/bin")
set(ffmpeg_avutil_LIBRARY_TYPE_RELEASE UNKNOWN)
set(ffmpeg_avutil_IS_HOST_WINDOWS_RELEASE 0)
set(ffmpeg_avutil_RES_DIRS_RELEASE )
set(ffmpeg_avutil_DEFINITIONS_RELEASE )
set(ffmpeg_avutil_OBJECTS_RELEASE )
set(ffmpeg_avutil_COMPILE_DEFINITIONS_RELEASE )
set(ffmpeg_avutil_COMPILE_OPTIONS_C_RELEASE "")
set(ffmpeg_avutil_COMPILE_OPTIONS_CXX_RELEASE "")
set(ffmpeg_avutil_LIBS_RELEASE )
set(ffmpeg_avutil_SYSTEM_LIBS_RELEASE )
set(ffmpeg_avutil_FRAMEWORK_DIRS_RELEASE )
set(ffmpeg_avutil_FRAMEWORKS_RELEASE )
set(ffmpeg_avutil_DEPENDENCIES_RELEASE )
set(ffmpeg_avutil_SHARED_LINK_FLAGS_RELEASE )
set(ffmpeg_avutil_EXE_LINK_FLAGS_RELEASE )
set(ffmpeg_avutil_NO_SONAME_MODE_RELEASE FALSE)

so why ffmpeg_avutil_LIBS_RELEASE is not setted? Did I miss some configuration item? If I manually add a value, it works

memsharded commented 5 days ago

Hi @JamesPei

Some quick preliminary feedback:

I will be trying the package_type change, it can be the reason.

memsharded commented 5 days ago

I confirm, changing package_type = "library" works.

JamesPei commented 3 days ago

@memsharded thank you sir, it works after changing the package_type to library. so,does the package_type application mean that the -l option will not be generated? And, only projects that contain only executable and no any library files set to application?

memsharded commented 3 days ago

Package-type affects how information is propagated to consumers. If you have an application that requires a static-library, and someone does a requires to the application (in most cases it should be a tool_requires if intended to be used in the build), then the library information will not be propagated to the downstream consumers, only to the application.

Yes, in general, packages containing headers and libraries should be set to library if using options.shared or static-library/shared-library, even if they contain executables too. Packages that contain an executable only can be set to application.