conan-io / conan

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

[question] NMake: how to inject system libs of dependencies? #7098

Closed SpaceIm closed 4 years ago

SpaceIm commented 4 years ago

I'm creating a recipe for GDAL in CCI: https://github.com/conan-io/conan-center-index/pull/1722

GDAL has two build systems:

My question is about Visual Studio and how to properly inject informations about required system libs.

Currently I'm using tools.vcvars(self.settings) and VisualStudioBuildEnvironment:

with tools.chdir(os.path.join(self._source_subfolder, "gdal")):
    with tools.vcvars(self.settings):
        with tools.environment_append(VisualStudioBuildEnvironment(self).vars):
            self.run("nmake -f makefile.vc {}".format(" ".join(self._get_nmake_args())))

If at least one GDAL's dependency or transitive dependency depends on a system lib (such as user32, crypt32 etc) which are not direct dependencies of GDAL (ie explicitly linked in GDAL's NMake files), creation of shared GDAL fails (and also, static lib fails to link into GDAL's executables in NMake process) with undefined reference to symbols defined in those system libs.

I can manually inject those system libs by patching GDAL's NMake files like this (by listing union of all possible system libs of all possible dependency trees of GDAL):

@@ -1127,4 +1127,4 @@ EXTERNAL_LIBS =   $(OGDILIB) $(XERCES_LIB) $(EXPAT_LIB) $(OCI_LIB) $(PG_LIB) \
    $(LIBICONV_LIBRARY) $(WEBP_LIBS) $(TILEDB_LIBS) $(FGDB_LIB_LINK) $(FREEXL_LIBS) $(GTA_LIBS) \
    $(INGRES_LIB) $(LIBXML2_LIB) $(PCRE_LIB) $(MONGODB_LIB_LINK) $(MONGODBV3_LIB_LINK) $(CRYPTOPP_LIB) $(OPENSSL_LIB) $(CHARLS_LIB) ws2_32.lib \
    $(RDB_LIB) $(CRUNCH_LIB) \
-    kernel32.lib psapi.lib
+    kernel32.lib psapi.lib crypt32.lib msi.lib advapi32.lib user32.lib gdi32.lib shell32.lib secur32.lib wldap32.lib

and rely on others commands in GDAL's NMake files, like here:

$(GDAL_DLL): $(LIB_DEPENDS)
    call <<clean_main_build_output.bat
$(CLEAN_MAIN_BUILD_OUTPUT_CMDS)
<<
    link /nologo /dll $(OGR_INCLUDE) $(BASE_INCLUDE) $(LIBOBJ) \
        $(EXTERNAL_LIBS) gcore\Version.res \
         /out:$(GDAL_DLL) /implib:$(GDAL_LIB_NAME) $(LINKER_FLAGS)
    if exist $(GDAL_DLL).manifest mt -manifest $(GDAL_DLL).manifest -outputresource:$(GDAL_DLL);2

It works as a workaround, but it might break if a dependency suddenly decide to depend on another system lib. It's very fragile.

Is there a better way to properly inject those system libs using conan? Is there a way to have the list of system libs through deps_cpp_info, loop into all the dependency tree and inject this information?

memsharded commented 4 years ago

Hi @SpaceIm

yes, the deps_cpp_info has a system_libs attribute! Is that what you are looking for?

class HelloConan(ConanFile):
    requires = "poco/1.9.4"
    def build(self):
        system_libs = self.deps_cpp_info.system_libs
        print("SYSTEM LIBS %s" % system_libs)

Prints something like

SYSTEM LIBS ['iphlpapi', 'crypt32', 'msi', 'ws2_32', 'advapi32', 'user32', 'gdi32']

As a side note, I have been having a look at NMake, seeing if this could be better automated, but couldn't find anything better, there are no defined env-vars, not a way to define files, or standard variables...

SpaceIm commented 4 years ago

How ! Thanks a lot. I missed that. It's exactly what I was looking for.