Search code examples
c++pdfium

Unable to embed Pdfium - unresolved external symbol


After successfully building Pdfium as per Google's docs, I created a new C++ console app with VS2022 and tried to embed .lib resulting from Pdfium build but, it results in several unresolved symbols which, I guess, should be included in standard C++. As an example, here is two of the unresolved symbols:

fx_string.obj: error LNK2001: unresolved external symbol "void __cdecl std::Cr::__libcpp_verbose_abort(char const *,...)"
fpdf_parser_utility.obj : error LNK2001: unresolved external symbol "public: class std::Cr::basic_ostream<char,struct std::Cr::char_traits<char> > & __cdecl std::Cr::basic_ostream<char,struct std::Cr::char_traits<char> >::write(char const *,__int64)"

Steps taken:

  • build Pdfium following docs steps using below args.gn:
    use_goma = false
    is_debug = false
    pdf_use_skia = false
    pdf_enable_xfa = false
    pdf_enable_v8 = false
    pdf_is_standalone = false
    is_component_build = false
    pdf_is_complete_lib = true
    
  • Created a simple C++ console app with following content (copied from Getting started):
    #include <iostream>
    #include "../PDFium/public/fpdfview.h"
    
    int main()
    {
         std::cout << "PDFium test!\n";
    
         FPDF_LIBRARY_CONFIG config{};
         config.version = 2;
         config.m_pUserFontPaths = NULL;
         config.m_pIsolate = NULL;
         config.m_v8EmbedderSlot = 0;
    
         FPDF_InitLibraryWithConfig(&config);
    
         FPDF_DestroyLibrary();
    
         return 0;
    }
    
  • Copied Pdfium's public folder and resulting .lib file into a separate folder of the project, added necessary fields for linker and set C++ language spec to stdc++20; below, the corresponding tags in .vcxproj
    <!-- other tags -->
    <ClCompile>
         <!-- other tags -->
         <LanguageStandard>stdcpp20</LanguageStandard>
         <LanguageStandard_C>stdc11</LanguageStandard_C>
    </ClCompile>
    <!-- other tags -->
    <Link>
         <SubSystem>Console</SubSystem>
         <GenerateDebugInformation>true</GenerateDebugInformation>
         <AdditionalLibraryDirectories>$(SolutionDir)PDFium\x64</AdditionalLibraryDirectories>
         <AdditionalDependencies>PDFium.lib;%(AdditionalDependencies)</AdditionalDependencies>
    </Link>
    

(Unsuccessful) Tests did

  • Tried to compile PDFium with and without pdf_is_complete_lib = true, and thus, linking also single .objs, and v8 and xfa support
  • Tried to compile with Microsoft's clang-cl instead of MSBuild (the one provided by Visual Studio's Native Development pack)
  • Tried to compile with depot tools's clang-cl (currently, version 16.0.0)
  • Tried several options found here and there on SO and on internet

Any help is appreciated. Thanks in advance.


Solution

  • After days of failures and (vain) research, I finally found the solution, posted here for future reference.

    TL;DR;

    Depending on flags set in args.gn, Pdfium will need at least the following to successfully build and start the Getting Started example:

    • if is_component_build = false and, thus pdf_is_complete_lib = true, .obj files built from libc++ and winmm.lib
    • if is_component_build = true and, thus pdf_is_complete_lib = false, all .dll resulting from compilation, i.e.:
      • absl.dll
      • icuuc.dll
      • libc++.dll
      • partition_alloc.dll
      • pdfium.dll
      • zlib.dll

    Read also the important note below which tries to explain the difference between the two flags.

    Long description

    As per docs, once .ninja files has been generated as result of gn args <out_dir>, it can be seen which .obj, .lib and .dll files are needed to build pdfium_unitests.exe. Comparing those with the ones used to generate pdfium.lib (or pdfium.dll depending on flags, see below), it can be spotted the required libraries.

    With is_component_build = false and pdf_is_complete_lib = true:

    • start default compilation with ninja -C <out_dir>; this also builds libc++;
    • generate pdfium.lib via ninja -C <out_dir> pdfium (will only execute a link step)
    • (optionally) generate a libc++.lib using depot tool's lld-link which will contain all .obj files from compilation of libc++:
      "C:\path\to\pdfium\third_party\llvm-build\Release+Asserts\bin\lld-link.exe" /lib /OUT:libc++.lib /nologo /WX /ignore:4221 <space-separated list of .obj files in C:\out_dir\obj\buildtools\third_party\libc++\libc++>
      
    • link pdfium.lib, all libc++'s .obj files in C:\out_dir\obj\buildtools\third_party\libc++\libc++ (or, libc++.lib from previous step), winmm.lib in your project; e.g.:
      <Link>
          <!-- other options -->
          <AdditionalDependencies>pdfium.lib;libc++.lib;winmm.lib;%(AdditionalDependencies)</AdditionalDependencies>
      </Link>
      

    With is_component_build = true and pdf_is_complete_lib = false:

    • start compilation with ninja -C <out_dir> pdfium; will compile all the necessary .dlls along with .pdb and .lib files and put them in out_dir
    • link pdfium.lib in your project and copy emitted .dlls in output directory; assuming emitted files are in C:\pdfium\result, project's .vcxproj will look like this:
      <!-- ... -->
      <Link>
          <AdditionalLibraryDirectories>C:\pdfium\result</AdditionalLibraryDirectories>
          <AdditionalDependencies>pdfium.dll.lib</AdditionalDependencies>
      </Link>
      <!-- ... -->
      <PostBuildEvent>
          <Command>cmd /c "robocopy C:\pdfium\result $(OutDir) /xf *.lib &amp; if %errorlevel% geq 8 exit 0 else exit %errorlevel%"</Command>
      </PostBuildEvent>
      <!-- ... -->
      
      Note: the files you'll need are listed in tl;dr; section.
      Note 2: used robocopy because is more efficient than xcopy. Also, check on exit code is needed because of the value returned by robocopy

    Important note

    pdf_is_complete_lib and is_component_build will set, respectively, /MT and /MD flags during compilation.

    Although might not seem a problem, MSBuild will complain if the two types are mixed and thus, you'll need either to recompile Pdfium or change the way your code is generated via Project Properties > C/C++ > Code Generation > Runtime Library.

    For more information see Google docs, Microsoft docs on flags and linker warning and, eventually, this SO question which can help in choose the code generation that most suits your needs.