Skip to content

Conversation

@aymanalqadhi
Copy link
Contributor

@aymanalqadhi aymanalqadhi commented Jul 7, 2025

Why CMake?

  • Greatly improves IDE/LSP support. While almost all major IDEs have built-in support for CMake, CMake natively supports generating compile_commands.json files used by stand-alone tooling.
  • Simplifies the build process.
  • Reduces the amount of code to maintain (once this becomes mature enough), as CMake has generators for practically all widely-used build systems. The current custom build configurations for Visual Studio, and Xcode could be automatically generated using CMake.
  • Enables faster compilation/recompilation cycles, and may produce potentially faster generated code when used within CMake-based projects.
  • Provides better overall integration with modern tooling.

What does this PR add?

  • Near-complete support for building all modules using CMake-generated build systems (including test applications).
  • Ability to use either bundled third-party dependencies or system-installed ones.
  • A somewhat functional installation process, with proper (I think) handling of dyamic-linking RPATH values.
  • Ability to build both static, and shared libraries.
  • Configure-time generation of configuration headers (emulating current build system).
  • Cross-Platform building (needs testing). No platform-dependent tools were used, only CMake.
  • Options for almost all the things that can be customized in the current build system.

What is still needed?

  • Extended testing: Currently only validated (best-effort) on Linux x86_64. Initial groundwork exists for other platforms, but compilation will likely require platform-specific modifications.
  • Documentation generation support: Could be implemented using using CMake's built-in Doxygen module (should be relatively straightforward).
  • External dependency integration: While basic installation and application execution work, full support for using the library as an external (out-of-tree) dependency via CMake-native facilities (e.g., find_package in config mode) requires more work.

Suggestions for improvements

  • Dependency packaging approach: Why build bundled dependencies as separate libraries? Wouldn't this approach risk having conflicts with existing system-installed versions? Compiling them directly into dependents using CMake's object libraries would (in my opinion) be preferable. This would avoid the potential issues, while relatively simplifying packaging & installation.
  • Dependency include paths: Some modules reference third-party dependencies through relative paths (i.e., #include "../../third_party/some_dep/file.h"). This prevents transparent interchangeability between system-installed and bundled dependencies, complicating their management. Standardizing include paths would resolve this.

@CLAassistant
Copy link

CLAassistant commented Jul 7, 2025

CLA assistant check
All committers have signed the CLA.

@a-lerion
Copy link

a-lerion commented Sep 8, 2025

Because it is not up to the library to decide which runtime to use. The code that uses the library, on the other hand, is responsible for setting such values consistently.

It's not the library itself that decides it. The option needs to be explicitly set during project generation to have a different behaviour from the default one.

@aymanalqadhi
Copy link
Contributor Author

Because it is not up to the library to decide which runtime to use. The code that uses the library, on the other hand, is responsible for setting such values consistently.

It's not the library itself that decides it. The option needs to be explicitly set during project generation to have a different behaviour from the default one.

Setting CMAKE_MSVC_RUNTIME_LIBRARY inconsistently might have unexpected side effects. For example, if the library was used by an application that uses the dynamic runtime, but the library was linked with the static one, wouldn't there be two copies of the standard library functions that have a global state (i.e., malloc, strtok, etc.)? If so, that global state is no longer shared between the application and the library, which might cause serious bugs.

Leaving CMAKE_MSVC_RUNTIME_LIBRARY to the user to set is, in my opinion, the safest option.

@nanangizz
Copy link
Member

Try compiling with the latest commit, which copies files when creating a symbolic link fails.

Thanks, the configure step completed successfully now. Just FYI, there was a warning regarding the SDL2 probe (due to missing explicit settings and the absence of a CMake detection config?):

CMake Warning at pjmedia/CMakeLists.txt:9 (find_package):
  By not providing "FindSDL2.cmake" in CMAKE_MODULE_PATH this project has
  asked CMake to find a package configuration file provided by "SDL2", but
  CMake did not find one.

  Could not find a package configuration file provided by "SDL2" with any of
  the following names:

    SDL2Config.cmake
    sdl2-config.cmake

  Add the installation prefix of "SDL2" to CMAKE_PREFIX_PATH or set
  "SDL2_DIR" to a directory containing one of the above files.  If "SDL2"
  provides a separate development package or SDK, be sure it has been
  installed.
Call Stack (most recent call first):
  pjmedia/CMakeLists.txt:269 (import_if_or_set)

[!] SDL2 was not found. setting PJMEDIA_WITH_VIDEODEV_SDL to OFF

Then the generate step failed, looks like the COPY_ON_ERROR only copy the folder but not the content (tried to search around and found this), error message:

CMake Error at third_party/g7221/CMakeLists.txt:43 (target_sources):
  Cannot find source file:

    D:/work/tool/cmake-4.1.1-windows-x86_64/bin/third_party/g7221/include/g7221/common/basic_op.h

Perhaps it could check whether one or more header files exist. If not, it could copy all the G7221 header files. However, I'm unsure whether this approach (especially the copying part) would behave correctly if the symbolic link is created successfully.

@a-lerion
Copy link

a-lerion commented Sep 9, 2025

Leaving CMAKE_MSVC_RUNTIME_LIBRARY to the user to set is, in my opinion, the safest option.

That option is also for the user to set. It is simpler to set an ON/OFF option than to pass a generator expression.
Project libraries may be compiled for future use, without building an app that uses them.

Wouldn't there be two copies of the standard library functions?

This gives errors during linking.

@a-lerion
Copy link

a-lerion commented Sep 9, 2025

I used symbolic links to allow including headers from g7221 third-party library with g7221 prefix (the library expects to find its headers under g7221, which would require either adding third_party to the include path, or using this trick).

MSVC project files and Makefile that exist in the repo add third_party folder to include folders.

@aymanalqadhi
Copy link
Contributor Author

Leaving CMAKE_MSVC_RUNTIME_LIBRARY to the user to set is, in my opinion, the safest option.

That option is also for the user to set. It is simpler to set an ON/OFF option than to pass a generator expression. Project libraries may be compiled for future use, without building an app that uses them.

This is equivalent to adding a switch to select which compiler to use. Sure, it may work sometimes (since C has a stable ABI), but not every time. Giving users this option is like intentionally giving them a gun to shoot their feet.

Wouldn't there be two copies of the standard library functions?

This gives errors during linking.

No, it wont. Using shared libraries does not involve compile-time linkage, but rather the libraries are dynamically-linked (hence the name) at load-time by the loader.

Feel free to correct me if I am wrong.

@aymanalqadhi
Copy link
Contributor Author

I used symbolic links to allow including headers from g7221 third-party library with g7221 prefix (the library expects to find its headers under g7221, which would require either adding third_party to the include path, or using this trick).

MSVC project files and Makefile that exist in the repo add third_party folder to include folders.

The existing build system also (mostly) includes everything to everything. Dependency isolation is one of the biggest selling points of CMake, why give it up?

@a-lerion
Copy link

a-lerion commented Sep 9, 2025

No, it wont. Using shared libraries does not involve compile-time linkage, but rather the libraries are dynamically-linked (hence the name) at load-time by the loader.

Feel free to correct me if I am wrong.

E.g., linking a log4cplus library built with static runtime in a project built with dynamic runtime gives errors like the following in Visual Studio:

error LNK2005: "public: unsigned short const * __cdecl std::_Locinfo::_W_Getmonths(void)const " (?_W_Getmonths@_Locinfo@std@@QEBAPEBGXZ) already defined in msvcprtd.lib(MSVCP140D.dll)
error LNK2038: mismatch detected for 'RuntimeLibrary': value 'MTd_StaticDebug' doesn't match value 'MDd_DynamicDebug' in WindowProject.obj

@a-lerion
Copy link

a-lerion commented Sep 9, 2025

The existing build system also (mostly) includes everything to everything. Dependency isolation is one of the biggest selling points of CMake, why give it up?

Current solution looks a bit too much for this. I think changing #include directives would be better. It looks like only one file uses such includes, that start with g7221, third_party/g7221/common/defs.h, and it includes files in the same folder.

@nanangizz
Copy link
Member

Update on MSVC, it is now built successfully for the libs & and the pjsua app, however it is still failed on test apps (pjlib-test, pjlib-util-test, pjnath-test) with this error message:

1>MSVCRTD.lib(exe_main.obj) : error LNK2019: unresolved external symbol main referenced in function "int __cdecl invoke_main(void)" (?invoke_main@@YAHXZ)

The CMakeFile for test apps seem to use main_win32.c for Windows platforms, I think this is the cause. Replacing it with main.c should fix this. In fact the existing MSVC projects use main.c. IIRC the main_win32.c is for the old & deprecated Windows Mobile that does not have console UI.

Btw, pjmedia-test & pjsip-test projects are missing, I believe because of the typos (see inline code comments).

@a-lerion
Copy link

The CMakeFile for test apps seem to use main_win32.c for Windows platforms, I think this is the cause.

Yes, its use requires WIN32 option for add_executable as it's not a console app but rather a window app.

Replacing it with main.c should fix this. In fact the existing MSVC projects use main.c.

Agree.

@aymanalqadhi
Copy link
Contributor Author

No, it wont. Using shared libraries does not involve compile-time linkage, but rather the libraries are dynamically-linked (hence the name) at load-time by the loader.
Feel free to correct me if I am wrong.

E.g., linking a log4cplus library built with static runtime in a project built with dynamic runtime gives errors like the following in Visual Studio:

error LNK2005: "public: unsigned short const * __cdecl std::_Locinfo::_W_Getmonths(void)const " (?_W_Getmonths@_Locinfo@std@@QEBAPEBGXZ) already defined in msvcprtd.lib(MSVCP140D.dll)
error LNK2038: mismatch detected for 'RuntimeLibrary': value 'MTd_StaticDebug' doesn't match value 'MDd_DynamicDebug' in WindowProject.obj

It seems that MSVC's linker does check for conflicting symbols (in this case, RuntimeLibrary, which was put specifically to prevent what I was talking about). Other compilers might handle this differently.

@aymanalqadhi
Copy link
Contributor Author

The existing build system also (mostly) includes everything to everything. Dependency isolation is one of the biggest selling points of CMake, why give it up?

Current solution looks a bit too much for this. I think changing #include directives would be better. It looks like only one file uses such includes, that start with g7221, third_party/g7221/common/defs.h, and it includes files in the same folder.

I had to use that method to make sure this PR is add-only, without changing the current codebase. The code inside third_party/g7221 should not be modified if that module is to be updated sometime in the future.

@aymanalqadhi
Copy link
Contributor Author

The CMakeFile for test apps seem to use main_win32.c for Windows platforms, I think this is the cause. Replacing it with main.c should fix this. In fact the existing MSVC projects use main.c. IIRC the main_win32.c is for the old & deprecated Windows Mobile that does not have console UI.

Windows builds now should use main.c. Should Windows Phone builds still use main_win32.c?

Btw, pjmedia-test & pjsip-test projects are missing, I believe because of the typos (see inline code comments).

You're right.

@aymanalqadhi
Copy link
Contributor Author

@nanangizz While you're at it, can you try building with other toolchains like LLVM/Clang, MinGW, and/or CYGWIN?

@a-lerion
Copy link

a-lerion commented Sep 12, 2025

While you're at it, can you try building with other toolchains like LLVM/Clang, MinGW, and/or CYGWIN?

Tried Visual Studio with clangcl toolset for x64. webrtc_aec3 project succeeds after AVX2 architecture is specified, target_compile_options with /arch:AVX2. Otherwise fails with errors like the following:

third_party\webrtc_aec3\src\common_audio\resampler\sinc_resampler_avx2.cc(25,20): error : always_inline function '_mm256_setzero_ps' requires target feature 'avx', but would be inlined into function 'Convolve_AVX2' that is compiled without support for 'avx'
third_party\webrtc_aec3\src\common_audio\resampler\sinc_resampler_avx2.cc(34,17): error : always_inline function '_mm256_fmadd_ps' requires target feature 'fma', but would be inlined into function 'Convolve_AVX2' that is compiled without support for 'fma'
third_party\webrtc_aec3\src\common_audio\resampler\sinc_resampler_avx2.cc(46,34): error : '__builtin_ia32_vextractf128_ps256' needs target feature avx

Other binaries are built without problems.

@nanangizz
Copy link
Member

Yes, Windows Phone may also use main_win32.c, but I guess we can skip that for now (the platform itself has been deprecated btw).

FYI, I experienced issues configuring for MinGW (I am a newbie, remember? :), when I use cmake-gui (the same app I used for configuring for MSVC), with generator set to "MinGW Makefiles" & CMAKE_MAKE_PROGRAM=C:/msys64/usr/bin/make.exe, it failed:

The C compiler identification is GNU 14.2.0
The CXX compiler identification is GNU 14.2.0
Detecting C compiler ABI info
Detecting C compiler ABI info - failed
Check for working C compiler: C:/msys64/mingw64/bin/cc.exe
Check for working C compiler: C:/msys64/mingw64/bin/cc.exe - broken
CMake Error at D:/work/tool/cmake-4.1.1-windows-x86_64/share/cmake-4.1/Modules/CMakeTestCCompiler.cmake:67 (message):
  The C compiler

    "C:/msys64/mingw64/bin/cc.exe"

  is not able to compile a simple test program.

  It fails with the following output:

    Change Dir: 'D:/work/project/pjproject-tmp/cmake-mingw/CMakeFiles/CMakeScratch/TryCompile-qqqngy'
    
    Run Build Command(s): D:/work/tool/cmake-4.1.1-windows-x86_64/bin/cmake.exe -E env VERBOSE=1 C:/msys64/usr/bin/make.exe -f Makefile cmTC_76308/fast
    /usr/bin/make  -f CMakeFiles\cmTC_76308.dir\build.make CMakeFiles/cmTC_76308.dir/build
    ' m T C _ 7 6 3 0 8 . d i r '   i s   n o t   r e c o g n i z e d   a s   a n   i n t e r n a l   o r   e x t e r n a l   c o m m a n d , 
     o p e r a b l e   p r o g r a m   o r   b a t c h   f i l e . 

     make: *** [Makefile:133: cmTC_76308/fast] Error 1
    
  CMake will not be able to correctly generate this project.
Call Stack (most recent call first):
  CMakeLists.txt:3 (project)

Configuring incomplete, errors occurred!

I also tried to use cmake-3.30.5, installed using pacman -S cmake from MinGW64/Msys2 console, failed:

$ cmake -S D:/work/project/pjproject-tmp/ -B D:/work/project/pjproject-tmp/cmake-mingw/
-- The C compiler identification is unknown
-- The CXX compiler identification is unknown
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
CMake Error at /usr/share/cmake/Modules/CMakeDetermineCompilerABI.cmake:139 (file):
  file STRINGS file
  "D:/work/project/pjproject-tmp/D:/work/project/pjproject-tmp/cmake-mingw/CMakeFiles/3.30.5/CMakeDetermineCompilerABI_C.bin"
  cannot be read.
Call Stack (most recent call first):
  /usr/share/cmake/Modules/CMakeTestCCompiler.cmake:26 (CMAKE_DETERMINE_COMPILER_ABI)
  CMakeLists.txt:3 (project)


-- Check for working C compiler: /mingw64/bin/cc.exe - skipped
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
CMake Error at /usr/share/cmake/Modules/CMakeDetermineCompilerABI.cmake:139 (file):
  file STRINGS file
  "D:/work/project/pjproject-tmp/D:/work/project/pjproject-tmp/cmake-mingw/CMakeFiles/3.30.5/CMakeDetermineCompilerABI_CXX.bin"
  cannot be read.
Call Stack (most recent call first):
  /usr/share/cmake/Modules/CMakeTestCXXCompiler.cmake:26 (CMAKE_DETERMINE_COMPILER_ABI)
  CMakeLists.txt:3 (project)


-- Check for working CXX compiler: /mingw64/bin/c++.exe - skipped
CMake Warning at CMakeLists.txt:16 (message):
   ****************************************************
   * Building `pjproject` with CMake is experimental  *
   *--------------------------------------------------*
   * Tested platforms:                                *
   * - Linux x86_64                                   *
   ****************************************************

  Set `PJ_SKIP_EXPERIMENTAL_NOTICE=ON` to disable this warning


CMake Warning (dev) at /usr/share/cmake/Modules/GNUInstallDirs.cmake:253 (message):
  Unable to determine default CMAKE_INSTALL_LIBDIR directory because no
  target architecture is known.  Please enable at least one language before
  including GNUInstallDirs.
Call Stack (most recent call first):
  CMakeLists.txt:45 (include)
This warning is for project developers.  Use -Wno-dev to suppress it.

-- Looking for winsock.h
-- Looking for winsock.h - found
-- Looking for winsock2.h
-- Looking for winsock2.h - found
-- Looking for mswsock.h
-- Looking for mswsock.h - found
-- Looking for ws2tcpip.h
-- Looking for ws2tcpip.h - found
-- Performing Test CMAKE_HAVE_LIBC_PTHREAD
-- Performing Test CMAKE_HAVE_LIBC_PTHREAD - Success
-- Found Threads: TRUE
-- Check if the system is big endian
-- Searching 16 bit integer
-- Looking for sys/types.h
-- Looking for sys/types.h - found
-- Looking for stdint.h
-- Looking for stdint.h - found
-- Looking for stddef.h
-- Looking for stddef.h - found
-- Check size of unsigned short
CMake Error at /usr/share/cmake/Modules/CheckTypeSize.cmake:159 (file):
  file STRINGS file
  "D:/work/project/pjproject-tmp/pjlib/D:/work/project/pjproject-tmp/cmake-mingw/CMakeFiles/CheckTypeSize/CMAKE_SIZEOF_UNSIGNED_SHORT.bin"
  cannot be read.
Call Stack (most recent call first):
  /usr/share/cmake/Modules/CheckTypeSize.cmake:272 (__check_type_size_impl)
  /usr/share/cmake/Modules/TestBigEndian.cmake:55 (CHECK_TYPE_SIZE)
  /usr/share/cmake/Modules/TestBigEndian.cmake:37 (__TEST_BIG_ENDIAN_LEGACY_IMPL)
  pjlib/CMakeLists.txt:341 (test_big_endian)

...

-- Configuring incomplete, errors occurred!

Any idea?

@a-lerion
Copy link

a-lerion commented Sep 15, 2025

@nanangizz

FYI, I experienced issues configuring for MinGW (I am a newbie, remember? :), when I use cmake-gui (the same app I used for configuring for MSVC), with generator set to "MinGW Makefiles" & CMAKE_MAKE_PROGRAM=C:/msys64/usr/bin/make.exe, it failed

What installation package have you used? Does it correspond to your platform?
Have you installed a compiler package or was it available?
Have you used cmake from a package installer or from a host OS?

I have installed msys2-x86_64-20250830.exe, installed packages mingw-w64-x86_64-gcc git cmake ninja openssl-devel.
After cloning a repository generating project files for ninja succeeded.
Compilation failed for some files in the webrtc_aec3 target.

third_party/webrtc_aec3/src/rtc_base/platform_thread_types.h:47:1: error: 'PlatformThreadId' does not name a type
   47 | PlatformThreadId CurrentThreadId();
third_party/webrtc_aec3/src/rtc_base/platform_thread_types.h:52:1: error: 'PlatformThreadRef' does not name a type
   52 | PlatformThreadRef CurrentThreadRef();

After adding WEBRTC_WIN compiler definition for MINGW compilation failed with

third_party/webrtc_aec3/src/rtc_base/platform_thread_types.cc:102:3: error: '__try' was not declared in this scope
third_party/webrtc_aec3/src/rtc_base/platform_thread_types.cc:105:5: error: '__except' was not declared in this scope

__try/__except are Microsoft-specific.

@nanangizz
Copy link
Member

What installation package have you used? Does it correspond to your platform? Have you installed a compiler package or was it available? Have you used cmake from a package installer or from a host OS?

I already have MinGW/msys2 installed for development, with compiler package.
Then, I install CMake, tried both without luck:

  • CMake for Windows, the host OS, (version 4.1.1), there are generator options "MinGW Makefile" & "msys2 Makefile", tried both options.
  • CMake for MinGW (version 3.30.5, installed from MinGW console using packet manager pacman).

Haven't tried to generate project files for ninja though.

@a-lerion
Copy link

Tried with host OS cmake and got similar errors.
MinGW cmake worked fine. Tried relative paths with a copy of repository inside MinGW user home folder. And tried absolute paths outside of MinGW using / rather than :, e.g.
/D/work/project/pjproject-tmp/cmake-mingw/ instead of D:/work/project/pjproject-tmp/cmake-mingw/

@nanangizz
Copy link
Member

I can generate the project files on MinGW successfully now. However, in make, I got the same error as you've described above ('PlatformThreadId' does not name a type). Also if I tried to run make again, it always failed:

pjlib/CMakeFiles/pjlib.dir/compiler_depend.make:4: *** multiple target patterns.  Stop.
make[1]: *** [CMakeFiles/Makefile2:1133: pjlib/CMakeFiles/pjlib.dir/all] Error 2
make: *** [Makefile:146: all] Error 2

@nanangizz
Copy link
Member

Just realized that using the existing Makefile, WebRTC AEC3 on MinGW (e.g: ./configure --enable-libwebrtc-aec3) fails too:

../../webrtc_aec3/src//rtc_base/platform_thread_types.cc:46:10: error: invalid cast from type 'pthread_t' {aka 'long long unsigned int'} to type 'rtc::PlatformThreadId' {aka 'long long int'}
   46 |   return reinterpret_cast<PlatformThreadId>(pthread_self());

So I guess we can just disabled the WebRTC AEC3 for MinGW for now?

@sauwming sauwming merged commit 9afe6e9 into pjsip:master Sep 24, 2025
32 of 42 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants