You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
CMake: support cross-compiling for Generic targets
Motivation
pjproject is being ported to an RTOS (Zephyr, on Cortex-M). Zephyr builds with CMake, and the plan is for its module to reuse the per-library CMakeLists.txt in this tree rather than maintain a fourth source list. That requires the CMake build to configure cleanly for a target CMake calls Generic: no operating system underneath, or an RTOS that brings its own build system. On master this fails: project() demands a C++ compiler, find_library(m REQUIRED) aborts in the bundled codecs, every bundled dependency is configured whether or not the consumer can use it, and pjsip-apps declares console executables unconditionally.
This PR makes the Generic configure succeed with a plain GCC cross toolchain and adds a sample toolchain file and CI jobs for it. Every existing platform is left identical: a native Linux configure after this change has the same notices, the same target list and the same object rules as master, and the full build is warning-free. A full cross build is not expected yet: pjlib still needs a socket layer and pthreads, which an RTOS supplies.
Changes
project() now declares LANGUAGES C. New option PJ_WITH_CXX (default ON) calls enable_language(CXX) and gates everything that needs C++: pjsua2, pjsua2-test, pjsua2_demo and the SWIG bindings; pjlib's atomic_queue.cpp and the Apple c++ runtime link; the C++ sources and backend options in pjmedia (WebRTC AEC3, libyuv, Lyra, OpenH264, Oboe, WASAPI, DirectShow, Android MediaCodec); the bundled webrtc_aec3 and yuv dependencies; and bundled WebRTC on x86, whose CPU-feature detection (cpu_features.cc) is C++. Off, the build is pure C.
New macro PJ_HAS_ATOMIC_QUEUE (default 1 in pj/config.h). The CMake build writes it into the generated, installed pj/compat/os_auto.h, 0 when C++ is off, and pj/atomic_queue.h declares the API only when it is set, so a C consumer of a C-only install fails to compile rather than to link. The API is used only by the Android Oboe and MediaCodec backends, which are C++ themselves. The MSVC project never compiled atomic_queue.cpp either and could use the same macro; not changed here.
New option PJ_BUILD_APPS (default ON) controls add_subdirectory(pjsip-apps), so a build of ALL for a target without a console does not try to link programs.
Every PJ_DEP_<X> option gains a third provider value, none, which leaves the dependency out; the pjmedia features that need it switch themselves off through their existing TARGET Pj::Dep::<X> checks. The provider loop now resets dep_target per iteration; without that, a left-out dependency would have been aliased to the previous one's target.
The six bundled libraries that used find_library(MATH_LIBRARY m REQUIRED) now use the optional form pjlib already uses, and they and pjmedia link m by name so a cross toolchain resolves it through its multilib specs rather than the default-multilib archive find_library() returns; on a Generic system m is linked even when find_library() misses it, as pjlib already did. On Linux the link lines have the same tokens as before. pjlib no longer forces PJLIB_WITH_FLOATING_POINT off on a Generic system for a missing libm.
The install target list appends pjsua2 and pjsua only when those targets exist, replacing the fixed list and the if(NOT IOS) for pjsua. Equivalent on every existing platform.
Architecture detection uses try_compile() instead of try_run(). The probe reports the architecture through a deliberate #error, so the run step never happened and its result was never read; try_run() only added cross-compiling rules to satisfy.
The SIMD compile check in pj_detect_arch_simd_ext() now actually runs for a Generic system. Its result variable was preset to true before the check, which makes check_c_source_compiles() skip it, so every ARM target was assumed to have NEON. On a Generic system the check no longer adds -mfpu=neon itself, which would override the target's FPU flags and compile NEON for a Cortex-M33 with FPU; it tests __ARM_NEON, so the CPU flags given to the toolchain decide. The cached result is versioned so a build directory configured by an older probe is re-probed. Hosted platforms keep the preset; see the open issue below.
pjmedia sets the effective value of every PJMEDIA_WITH_* option into the parent scope, and the package export goes by the Pj::Dep::<X> targets rather than the PJ_DEP_<X> cache entries, so PjConfig.cmake describes what was built after a reconfigure that turned C++ or a provider off. A forced PJMEDIA_WITH_RESAMPLE=none is reset when the option is declared again, so re-enabling the provider restores libresample.
New cmake/toolchains/arm-none-eabi.cmake: Generic/arm, compiler prefix and CPU flags from PJ_TOOLCHAIN_PREFIX and PJ_TARGET_CPU_FLAGS (-D or environment on the first configure, remembered in the cache from then on; defaults arm-none-eabi- and -mcpu=cortex-m33 -mthumb), compile-only try_compile, and CMAKE_FIND_ROOT_PATH restricted to the toolchain's own tree so host libraries and CMake packages are never picked up. The tree is GCC's sysroot when it reports one; otherwise it is the innermost directory named after the compiler's triplet on the path of its libc.a, which covers the Zephyr SDK and Debian's gcc-arm-none-eabi layouts, and an unrecognised layout stops with an error naming -DCMAKE_FIND_ROOT_PATH rather than searching the host.
tests/cmake builds its pjsua2 consumer only when the install provides Pj::pjsua2.
New CI jobs in ci-linux.yml. cmake-build-c-only: full native build with PJ_WITH_CXX=OFF, install, and the C consumer test. cmake-reconfigure: tests/cmake/reconfigure.sh configures a directory with one option set and reconfigures it with another, and compares every generated file against a fresh configure with the second set, for ten transitions. cmake-cross-arm-none-eabi: cross configure with the Ubuntu Arm GNU toolchain packages, asserting the toolchain's libm, no C++ compiler and no NEON flags; builds resample and checks its ELF attributes say Cortex-M33; compiles the 34 pjlib files that need only the C library's headers; configures a hard-float Cortex-M33 from a cache seeded with the old probe's NEON result and asserts no NEON; configures with every bundled dependency set to none.
Add -DPJ_TOOLCHAIN_PREFIX=/path/to/arm-zephyr-eabi- for a toolchain that is not on PATH, and -DPJ_TARGET_CPU_FLAGS=... for another core or FPU. For a footprint configuration add -DPJ_DEP_G7221=none -DPJ_DEP_GSM=none -DPJ_DEP_ILBC=none -DPJ_DEP_SPEEX=none -DPJ_DEP_WEBRTC=none -DPJMEDIA_WITH_VIDEO=OFF; SRTP and Resample can be set to none too.
Validation
Native Linux, defaults, fresh build directories against a master baseline: configure notices identical, --target help identical, the object rules in every generated build.make identical, all 34 link commands have the same token set. Full build exit 0, zero compiler warnings, pjsua2, pjsua2_demo and pjsua2-test built.
Native Linux with -DPJ_WITH_CXX=OFF: full build exit 0, zero warnings, pjsua and all test programs built, no C++ compiler in the cache, no pjsua2/yuv/webrtc_aec3/webrtc targets; install and C consumer test pass.
Reconfigure guard: all ten transitions match a fresh configure on this branch; run against master it reports the stale ALSA and video dependencies in PjConfig.cmake.
Cross, Zephyr SDK 1.0.1 (GCC 14.3, picolibc) and Ubuntu gcc-arm-none-eabi 13.2 (newlib): configure and generate exit 0 with both, libm found inside each toolchain's tree, no host OpenSSL config picked up, PJ_HAS_FLOATING_POINT 1, little endian, PJ_IOQUEUE_IMP_SELECT, PJ_HAS_SSL_SOCK 0. Also with -DPJ_WITH_CXX=ON (g++ detected) and with every PJ_DEP_*=none including SRTP and Resample.
Cross build: resample and gsm build for Cortex-M33. Of the 53 pjlib sources configured for the target, 34 compile and 19 do not: every file that includes pj/sock.h, whose address types need netinet/in.h, plus os_core_unix.c for pthread.h; an RTOS provides both. webrtc compiles with the plain C sources down to one error, once() in spl_init.c, which needs WEBRTC_POSIX or _WIN32 defined.
Cross, Cortex-M33 with -mfpu=fpv5-sp-d16 -mfloat-abi=hard: no NEON selected, also when the cache holds the old probe's NEON result. A Generic Cortex-A7 with -mfpu=neon-vfpv4 -mfloat-abi=hard selects NEON without an added flag and its NEON objects compile.
Windows, Android and iOS were not run here. All three keep PJ_WITH_CXX at its default ON, so enable_language(CXX) runs right after project() and every C++ target, source and option is exactly as before; the cmake_dependent_option conditions only gained a true term. The Android CI asserts Oboe, JNI, OpenGL, Android video and MediaCodec stay on, and atomic_queue.cpp, which Oboe and MediaCodec need, is still compiled. On iOS, pjsip-apps was already skipped and pjsua was already left out of the install list; the sample-libs block already tolerates absent targets.
Open issues
The SIMD compile check still does not run on hosted platforms, where the preset result is kept deliberately. The arm architecture name covers both cores with NEON and cores without it, so a 32-bit Linux build on an armv6 board would still get NEON sources. Letting the check run everywhere is a one-line change (move the preset into an else() branch), held back until a CI run confirms the check passes on Mac arm64 (-march=armv8-a+simd) and Android.
Options that depend on another option or on a found package keep stale values across reconfigures of the same build directory, on master as well. cmake_dependent_option() leaves the previous choice in the cache and sets the effective value in its own directory only, while pj_force_set() writes the cache and the forced value outlives its cause; the root reads option variables from both when it writes PjConfig.cmake. This PR fixes the cases it made reachable (PJ_WITH_CXX, the none providers, the resampling option) and adds a CI job that compares a reconfigure against a fresh configure for ten option transitions. The fundamental fix is separate work: derive the package config from the exported targets rather than from option variables, and let the helpers in cmake/Pj/Option.cmake own forcing and resetting.
The libpjproject.pc the CMake build installs has an empty Cflags line, so a pkg-config consumer of a CMake install gets neither the include path nor PJ_AUTOCONF. Pre-existing and separate.
For an RTOS port, separately: no PJ_M_* macro is emitted for the arm canonical name, so pj/config.h falls back to its own __arm__ block; libsrtp's static srtp_config.h hard-codes its platform header guards and stops every srtp object with "Platform not recognized"; WebRTC's spl_init.c needs WEBRTC_POSIX defined; and FindThreads reports pthreads found under compile-only probing, harmless for static libraries.
Propagate custom toolchain variables into nested try_compile projects
cmake/toolchains/arm-none-eabi.cmake:26
PJ_TOOLCHAIN_PREFIX and PJ_TARGET_CPU_FLAGS are custom toolchain variables, but they are not propagated into nested try_compile() projects. When the documented -DPJ_TOOLCHAIN_PREFIX=... form is used, a nested configure can reload this file without that value and overwrite CMAKE_C_COMPILER with the default arm-none-eabi-gcc, breaking compiler checks or architecture/SIMD probes. Register both variables with CMAKE_TRY_COMPILE_PLATFORM_VARIABLES.
Stale provider cache entries corrupt export metadata when C++ is disabled
third_party/CMakeLists.txt:64
When C++ is disabled, this provider option is no longer instantiated, but an existing or explicitly supplied PJ_DEP_YUV cache entry remains defined. The package-export code later treats any defined PJ_DEP_YUV=system|bundled as selected, so a pure-C install can incorrectly require YUV (or warn about a bundled target that was never created). The same stale-cache path affects the newly gated WebRTC/WebRTC_AEC3 providers; collect export metadata only for providers whose Pj::Dep::* target exists, or clear unavailable provider cache entries.
CI does not verify the Generic SIMD probe or exclude NEON
.github/workflows/ci-linux.yml:871
This check only validates the CPU flags on resample; it does not verify the new Generic SIMD probe. If that probe regresses and selects NEON for Cortex-M33 again, this job still passes because WebRTC is never built or inspected. Assert that the generated WebRTC rules omit the NEON sources (or assert the probe's cached result) so the regression this PR fixes is covered.
This root path lets find_library(MATH_LIBRARY m) resolve a concrete default-multilib archive. Although pjlib and the bundled codecs now link m by name, pjmedia/CMakeLists.txt:671-676 still records the absolute MATH_LIBRARY; for a static Cortex-M33 build, the final link can therefore pull the toolchain's base ARM libm instead of the multilib selected by -mcpu/-mthumb. Update pjmedia's math dependency to use m by name with the same Generic fallback.
Stale PJ_WITH_CXX value leaks into generated package config
pjmedia/CMakeLists.txt:205
When an existing build is reconfigured from PJ_WITH_CXX=ON to OFF, cmake_dependent_option() preserves the prior ON cache choice and exposes OFF only in this directory. The root directory therefore expands the stale ON value into PjConfig.cmake (cmake/Pj/PjConfig.cmake.in:113-115), so a C-only installed package still requires Lyra. Propagate the effective post-import value to the parent scope.
This issue also appears in the following locations of the same file:
Link the resolved MATH_LIBRARY path instead of literal m
pjlib/CMakeLists.txt:299
Using the literal m discards the path returned by find_library(). On a non-Generic build where m is supplied through CMAKE_LIBRARY_PATH or a non-default SDK path, configuration succeeds but the linker searches its defaults and can select the wrong library or fail. Keep m only for the Generic multilib fallback; otherwise link the resolved MATH_LIBRARY as before.
Link the resolved MATH_LIBRARY path instead of literal m
third_party/g7221/CMakeLists.txt:85
Using the literal m discards the path returned by find_library(). On a non-Generic build where m is supplied through CMAKE_LIBRARY_PATH or a non-default SDK path, configuration succeeds but the linker searches its defaults and can select the wrong library or fail. Keep m only for the Generic multilib fallback; otherwise link the resolved MATH_LIBRARY as before.
Link the resolved MATH_LIBRARY path instead of literal m
third_party/ilbc/CMakeLists.txt:73
Using the literal m discards the path returned by find_library(). On a non-Generic build where m is supplied through CMAKE_LIBRARY_PATH or a non-default SDK path, configuration succeeds but the linker searches its defaults and can select the wrong library or fail. Keep m only for the Generic multilib fallback; otherwise link the resolved MATH_LIBRARY as before.
Link the resolved MATH_LIBRARY path instead of literal m
third_party/speex/CMakeLists.txt:86
Using the literal m discards the path returned by find_library(). On a non-Generic build where m is supplied through CMAKE_LIBRARY_PATH or a non-default SDK path, configuration succeeds but the linker searches its defaults and can select the wrong library or fail. Keep m only for the Generic multilib fallback; otherwise link the resolved MATH_LIBRARY as before.
Link the resolved MATH_LIBRARY path instead of literal m
third_party/webrtc/CMakeLists.txt:145
Using the literal m discards the path returned by find_library(). On a non-Generic build where m is supplied through CMAKE_LIBRARY_PATH or a non-default SDK path, configuration succeeds but the linker searches its defaults and can select the wrong library or fail. Keep m only for the Generic multilib fallback; otherwise link the resolved MATH_LIBRARY as before.
Link the resolved MATH_LIBRARY path instead of literal m
third_party/webrtc_aec3/CMakeLists.txt:228
Using the literal m discards the path returned by find_library(). On a non-Generic build where m is supplied through CMAKE_LIBRARY_PATH or a non-default SDK path, configuration succeeds but the linker searches its defaults and can select the wrong library or fail. Keep m only for the Generic multilib fallback; otherwise link the resolved MATH_LIBRARY as before.
Link the resolved MATH_LIBRARY path instead of literal m
third_party/yuv/CMakeLists.txt:105
Using the literal m discards the path returned by find_library(). On a non-Generic build where m is supplied through CMAKE_LIBRARY_PATH or a non-default SDK path, configuration succeeds but the linker searches its defaults and can select the wrong library or fail. Keep m only for the Generic multilib fallback; otherwise link the resolved MATH_LIBRARY as before.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
CMake: support cross-compiling for Generic targets
Motivation
pjproject is being ported to an RTOS (Zephyr, on Cortex-M). Zephyr builds with CMake, and the plan is for its module to reuse the per-library
CMakeLists.txtin this tree rather than maintain a fourth source list. That requires the CMake build to configure cleanly for a target CMake callsGeneric: no operating system underneath, or an RTOS that brings its own build system. On master this fails:project()demands a C++ compiler,find_library(m REQUIRED)aborts in the bundled codecs, every bundled dependency is configured whether or not the consumer can use it, and pjsip-apps declares console executables unconditionally.This PR makes the Generic configure succeed with a plain GCC cross toolchain and adds a sample toolchain file and CI jobs for it. Every existing platform is left identical: a native Linux configure after this change has the same notices, the same target list and the same object rules as master, and the full build is warning-free. A full cross build is not expected yet: pjlib still needs a socket layer and pthreads, which an RTOS supplies.
Changes
project()now declaresLANGUAGES C. New optionPJ_WITH_CXX(default ON) callsenable_language(CXX)and gates everything that needs C++: pjsua2, pjsua2-test, pjsua2_demo and the SWIG bindings; pjlib'satomic_queue.cppand the Applec++runtime link; the C++ sources and backend options in pjmedia (WebRTC AEC3, libyuv, Lyra, OpenH264, Oboe, WASAPI, DirectShow, Android MediaCodec); the bundled webrtc_aec3 and yuv dependencies; and bundled WebRTC on x86, whose CPU-feature detection (cpu_features.cc) is C++. Off, the build is pure C.PJ_HAS_ATOMIC_QUEUE(default 1 inpj/config.h). The CMake build writes it into the generated, installedpj/compat/os_auto.h, 0 when C++ is off, andpj/atomic_queue.hdeclares the API only when it is set, so a C consumer of a C-only install fails to compile rather than to link. The API is used only by the Android Oboe and MediaCodec backends, which are C++ themselves. The MSVC project never compiledatomic_queue.cppeither and could use the same macro; not changed here.PJ_BUILD_APPS(default ON) controlsadd_subdirectory(pjsip-apps), so a build of ALL for a target without a console does not try to link programs.PJ_DEP_<X>option gains a third provider value,none, which leaves the dependency out; the pjmedia features that need it switch themselves off through their existingTARGET Pj::Dep::<X>checks. The provider loop now resetsdep_targetper iteration; without that, a left-out dependency would have been aliased to the previous one's target.find_library(MATH_LIBRARY m REQUIRED)now use the optional form pjlib already uses, and they and pjmedia linkmby name so a cross toolchain resolves it through its multilib specs rather than the default-multilib archivefind_library()returns; on a Generic systemmis linked even whenfind_library()misses it, as pjlib already did. On Linux the link lines have the same tokens as before. pjlib no longer forcesPJLIB_WITH_FLOATING_POINToff on a Generic system for a missing libm.pjsua2andpjsuaonly when those targets exist, replacing the fixed list and theif(NOT IOS)for pjsua. Equivalent on every existing platform.try_compile()instead oftry_run(). The probe reports the architecture through a deliberate#error, so the run step never happened and its result was never read;try_run()only added cross-compiling rules to satisfy.pj_detect_arch_simd_ext()now actually runs for a Generic system. Its result variable was preset to true before the check, which makescheck_c_source_compiles()skip it, so every ARM target was assumed to have NEON. On a Generic system the check no longer adds-mfpu=neonitself, which would override the target's FPU flags and compile NEON for a Cortex-M33 with FPU; it tests__ARM_NEON, so the CPU flags given to the toolchain decide. The cached result is versioned so a build directory configured by an older probe is re-probed. Hosted platforms keep the preset; see the open issue below.PJMEDIA_WITH_*option into the parent scope, and the package export goes by thePj::Dep::<X>targets rather than thePJ_DEP_<X>cache entries, soPjConfig.cmakedescribes what was built after a reconfigure that turned C++ or a provider off. A forcedPJMEDIA_WITH_RESAMPLE=noneis reset when the option is declared again, so re-enabling the provider restores libresample.cmake/toolchains/arm-none-eabi.cmake:Generic/arm, compiler prefix and CPU flags fromPJ_TOOLCHAIN_PREFIXandPJ_TARGET_CPU_FLAGS(-Dor environment on the first configure, remembered in the cache from then on; defaultsarm-none-eabi-and-mcpu=cortex-m33 -mthumb), compile-onlytry_compile, andCMAKE_FIND_ROOT_PATHrestricted to the toolchain's own tree so host libraries and CMake packages are never picked up. The tree is GCC's sysroot when it reports one; otherwise it is the innermost directory named after the compiler's triplet on the path of itslibc.a, which covers the Zephyr SDK and Debian'sgcc-arm-none-eabilayouts, and an unrecognised layout stops with an error naming-DCMAKE_FIND_ROOT_PATHrather than searching the host.tests/cmakebuilds its pjsua2 consumer only when the install providesPj::pjsua2.ci-linux.yml.cmake-build-c-only: full native build withPJ_WITH_CXX=OFF, install, and the C consumer test.cmake-reconfigure:tests/cmake/reconfigure.shconfigures a directory with one option set and reconfigures it with another, and compares every generated file against a fresh configure with the second set, for ten transitions.cmake-cross-arm-none-eabi: cross configure with the Ubuntu Arm GNU toolchain packages, asserting the toolchain's libm, no C++ compiler and no NEON flags; buildsresampleand checks its ELF attributes say Cortex-M33; compiles the 34 pjlib files that need only the C library's headers; configures a hard-float Cortex-M33 from a cache seeded with the old probe's NEON result and asserts no NEON; configures with every bundled dependency set tonone.Cross configure
Add
-DPJ_TOOLCHAIN_PREFIX=/path/to/arm-zephyr-eabi-for a toolchain that is not on PATH, and-DPJ_TARGET_CPU_FLAGS=...for another core or FPU. For a footprint configuration add-DPJ_DEP_G7221=none -DPJ_DEP_GSM=none -DPJ_DEP_ILBC=none -DPJ_DEP_SPEEX=none -DPJ_DEP_WEBRTC=none -DPJMEDIA_WITH_VIDEO=OFF; SRTP and Resample can be set tononetoo.Validation
--target helpidentical, the object rules in every generatedbuild.makeidentical, all 34 link commands have the same token set. Full build exit 0, zero compiler warnings, pjsua2, pjsua2_demo and pjsua2-test built.-DPJ_WITH_CXX=OFF: full build exit 0, zero warnings, pjsua and all test programs built, no C++ compiler in the cache, no pjsua2/yuv/webrtc_aec3/webrtc targets; install and C consumer test pass.PjConfig.cmake.gcc-arm-none-eabi13.2 (newlib): configure and generate exit 0 with both, libm found inside each toolchain's tree, no host OpenSSL config picked up,PJ_HAS_FLOATING_POINT 1, little endian,PJ_IOQUEUE_IMP_SELECT,PJ_HAS_SSL_SOCK 0. Also with-DPJ_WITH_CXX=ON(g++ detected) and with everyPJ_DEP_*=noneincluding SRTP and Resample.resampleandgsmbuild for Cortex-M33. Of the 53 pjlib sources configured for the target, 34 compile and 19 do not: every file that includespj/sock.h, whose address types neednetinet/in.h, plusos_core_unix.cforpthread.h; an RTOS provides both.webrtccompiles with the plain C sources down to one error,once()inspl_init.c, which needsWEBRTC_POSIXor_WIN32defined.-mfpu=fpv5-sp-d16 -mfloat-abi=hard: no NEON selected, also when the cache holds the old probe's NEON result. A Generic Cortex-A7 with-mfpu=neon-vfpv4 -mfloat-abi=hardselects NEON without an added flag and its NEON objects compile.PJ_WITH_CXXat its default ON, soenable_language(CXX)runs right afterproject()and every C++ target, source and option is exactly as before; thecmake_dependent_optionconditions only gained a true term. The Android CI asserts Oboe, JNI, OpenGL, Android video and MediaCodec stay on, andatomic_queue.cpp, which Oboe and MediaCodec need, is still compiled. On iOS, pjsip-apps was already skipped andpjsuawas already left out of the install list; the sample-libs block already tolerates absent targets.Open issues
armarchitecture name covers both cores with NEON and cores without it, so a 32-bit Linux build on an armv6 board would still get NEON sources. Letting the check run everywhere is a one-line change (move the preset into anelse()branch), held back until a CI run confirms the check passes on Mac arm64 (-march=armv8-a+simd) and Android.cmake_dependent_option()leaves the previous choice in the cache and sets the effective value in its own directory only, whilepj_force_set()writes the cache and the forced value outlives its cause; the root reads option variables from both when it writesPjConfig.cmake. This PR fixes the cases it made reachable (PJ_WITH_CXX, thenoneproviders, the resampling option) and adds a CI job that compares a reconfigure against a fresh configure for ten option transitions. The fundamental fix is separate work: derive the package config from the exported targets rather than from option variables, and let the helpers incmake/Pj/Option.cmakeown forcing and resetting.libpjproject.pcthe CMake build installs has an emptyCflagsline, so a pkg-config consumer of a CMake install gets neither the include path norPJ_AUTOCONF. Pre-existing and separate.PJ_M_*macro is emitted for thearmcanonical name, sopj/config.hfalls back to its own__arm__block; libsrtp's staticsrtp_config.hhard-codes its platform header guards and stops every srtp object with "Platform not recognized"; WebRTC'sspl_init.cneedsWEBRTC_POSIXdefined; and FindThreads reports pthreads found under compile-only probing, harmless for static libraries.