Skip to content

Conversation

@otegami
Copy link
Contributor

@otegami otegami commented Nov 27, 2025

Issue

On MSYS2 environments, installing gems that depend on pkgconf might fail because the pkgconf system package cannot be automatically installed.

Cause

The pkg-config gem was missing MSYS2 platform
configuration in its system package requirements.
Without this, the rubygems-requirements-system gem cannot automatically install the pkgconfg system
package on MSYS2 environments.

Solution

Add system: pkgconf: msys2: pkgconf to the gemspec requirements.
This works with rubygems-requirements-system which supports MSYS2 platform.

@kou
Copy link
Member

kou commented Nov 27, 2025

Could you add CI jobs?

gem install pkg-config never failed even if there is no pkgconf. If pkgconf doesn't exist, pkg-config just doesn't use search paths that are known by pkgconf.

@otegami
Copy link
Contributor Author

otegami commented Nov 27, 2025

This commit 3ab9fea raised the following error. I'm trying to fix it now on my fork.

Failure: test_cflags(PkgConfigTest)
D:/a/pkg-config/pkg-config/test/test-pkg-config.rb:202:in 'PkgConfigTest#assert_pkg_config'
D:/a/pkg-config/pkg-config/test/test-pkg-config.rb:37:in 'PkgConfigTest#test_cflags'
     34: 
     35:   def test_cflags
     36:     omit("Fragile on macOS") if RUBY_PLATFORM.include?("darwin")
  => 37:     assert_pkg_config("glib-2.0", ["--cflags"], @glib.cflags)
     38:   end
     39: 
     40:   def test_cflags_only_I
<"-ID:/a/_temp/msys64/clang64/include/glib-2.0 -I/tmp/local/lib/glib-2.0/include"> expected but was
<"-ID:/a/_temp/msys64/clang64/include/glib-2.0 -I/tmp/local/lib/glib-2.0/include -ID:/a/_temp/msys64/clang64/include">

diff:
? -ID:/a/_temp/msys64/clang64/include/glib-2.0 -I/tmp/local/lib/glib-2.0/include -ID:/a/_temp/msys64/clang64/include
Error: <"-ID:/a/_temp/msys64/clang64/include/glib-2.0 -I/tmp/local/lib/glib-2.0/include"> expected but was
<"-ID:/a/_temp/msys64/clang64/include/glib-2.0 -I/tmp/local/lib/glib-2.0/include -ID:/a/_temp/msys64/clang64/include">.

https://github.com/otegami/pkg-config/actions/runs/19723831055/job/56511296660#step:5:9

@otegami
Copy link
Contributor Author

otegami commented Nov 27, 2025

I investigated the test failure on MSYS2 and found that the pkg-config gem includes cflags from Requires.private dependencies, while pkgconf does not.
Should the pkg-config gem's behavior be changed to match pkgconf (i.e., exclude Requires.private cflags)?

Investigation details

The test_cflags and test_cflags_only_I tests fail on MSYS2 because the pkg-config gem outputs an extra include path compared to pkgconf.

ref: https://github.com/otegami/pkg-config/actions/runs/19724901555/job/56514480765#step:5:20

Debug output on MSYS2 (UCRT64)

=== glib-2.0.pc ===
prefix=/ucrt64
bindir=${prefix}/bin
datadir=${prefix}/share
includedir=${prefix}/include
libdir=${prefix}/lib
glib_genmarshal=${bindir}/glib-genmarshal.exe
gobject_query=${bindir}/gobject-query
glib_mkenums=${bindir}/glib-mkenums.exe
glib_valgrind_suppressions=${datadir}/glib-2.0/valgrind/glib.supp
Name: GLib
Description: C Utility Library
Version: 2.86.2
Requires.private: libpcre2-8 >= 10.32
Libs: -L${libdir} -lglib-2.0 -lintl
Libs.private: -lws2_32 -lole32 -lwinmm -lshlwapi -luuid -latomic -lm
Cflags: -I${includedir}/glib-2.0 -I${libdir}/glib-2.0/include

=== libpcre2-8.pc ===
(glib-2.0.pc has "Requires.private: libpcre2-8 >= 10.32")

# Package Information for pkg-config
prefix=/ucrt64
exec_prefix=${prefix}
libdir=${exec_prefix}/lib
includedir=${prefix}/include
Name: libpcre2-8
Description: PCRE2 - Perl compatible regular expressions C library (2nd API) with 8 bit character support
Version: 10.47
License: BSD-3-Clause WITH PCRE2-exception
Libs: -L${libdir} -lpcre2-8
Libs.private:
Cflags: -I${includedir}

=== pkgconf --cflags libpcre2-8 ===
(empty output - pkgconf filters out system include path)

=== pkgconf --cflags glib-2.0 ===
-ID:/a/_temp/msys64/ucrt64/include/glib-2.0 -ID:/a/_temp/msys64/ucrt64/lib/glib-2.0/include

=== pkg-config gem cflags ===
-ID:/a/_temp/msys64/ucrt64/include/glib-2.0 -ID:/a/_temp/msys64/ucrt64/lib/glib-2.0/include -ID:/a/_temp/msys64/ucrt64/include

Root cause

The pkg-config gem includes cflags from Requires.private dependencies, while pkgconf does not include them in --cflags output.

In the collect_cflags method, the gem uses all_required_packages which includes both Requires and Requires.private:

https://github.com/ruby-gnome/pkg-config/blob/ca9ad8a71aa5a6d77d0b2a2f6f8dcabc9cb63d6a/lib/pkg-config.rb#L530-L535

Current behavior comparison

pkgconf pkg-config gem
cflags Excludes Requires.private Includes Requires.private
libs Excludes Requires.private Excludes Requires.private

@kou
Copy link
Member

kou commented Nov 27, 2025

Hmm. I think that Requires.private's Cflags is needed in some situations but I can't remember them...

OK. Let's use Requires.private only for static linking mode.

Could you work on it as a separated PR?

It may be something like the following:

diff --git a/lib/pkg-config.rb b/lib/pkg-config.rb
index 197af8b..6ef996c 100644
--- a/lib/pkg-config.rb
+++ b/lib/pkg-config.rb
@@ -37,56 +37,58 @@ module PKGConfig
     /mswin/.match(RUBY_PLATFORM) and /^cl\b/.match(RbConfig::CONFIG["CC"])
   end
 
-  def package_config(package)
+  def package_config(package, **options)
     PackageConfig.new(package,
-                      :msvc_syntax => msvc?,
-                      :override_variables => @@override_variables,
-                      :paths => @@paths)
+                      {
+                        msvc_syntax: msvc?,
+                        override_variables: @@override_variables,
+                        paths: @@paths,
+                      }.merge(options))
   end
 
-  def exist?(pkg)
-    package_config(pkg).exist?
+  def exist?(pkg, **options)
+    package_config(pkg, **options).exist?
   end
 
-  def libs(pkg)
-    package_config(pkg).libs
+  def libs(pkg, **options)
+    package_config(pkg, **options).libs
   end
 
-  def libs_only_l(pkg)
-    package_config(pkg).libs_only_l
+  def libs_only_l(pkg, **options)
+    package_config(pkg, **options).libs_only_l
   end
 
-  def libs_only_L(pkg)
-    package_config(pkg).libs_only_L
+  def libs_only_L(pkg, **options)
+    package_config(pkg, **options).libs_only_L
   end
 
-  def cflags(pkg)
-    package_config(pkg).cflags
+  def cflags(pkg, **options)
+    package_config(pkg, **options).cflags
   end
 
-  def cflags_only_I(pkg)
-    package_config(pkg).cflags_only_I
+  def cflags_only_I(pkg, **options)
+    package_config(pkg, **options).cflags_only_I
   end
 
-  def cflags_only_other(pkg)
-    package_config(pkg).cflags_only_other
+  def cflags_only_other(pkg, **options)
+    package_config(pkg, **options).cflags_only_other
   end
 
-  def modversion(pkg)
-    package_config(pkg).version
+  def modversion(pkg, **options)
+    package_config(pkg, **options).version
   end
 
-  def description(pkg)
-    package_config(pkg).description
+  def description(pkg, **options)
+    package_config(pkg, **options).description
   end
 
-  def variable(pkg, name)
-    package_config(pkg).variable(name)
+  def variable(pkg, name, **options)
+    package_config(pkg, **options).variable(name)
   end
 
-  def check_version?(pkg, major=0, minor=0, micro=0)
+  def check_version?(pkg, major=0, minor=0, micro=0, **options)
     return false unless exist?(pkg)
-    ver = modversion(pkg).split(".").collect {|item| item.to_i}
+    ver = modversion(pkg, **options).split(".").collect {|item| item.to_i}
     (0..2).each {|i| ver[i] = 0 unless ver[i]}
 
     (ver[0] > major ||
@@ -95,7 +97,7 @@ module PKGConfig
       ver[2] >= micro))
   end
 
-  def have_package(pkg, major=nil, minor=0, micro=0)
+  def have_package(pkg, major=nil, minor=0, micro=0, **options)
     message = "#{pkg}"
     unless major.nil?
       message << " version (>= #{major}.#{minor}.#{micro})"
@@ -105,7 +107,7 @@ module PKGConfig
       if check_version?(pkg, major, minor, micro)
         "yes (#{modversion(pkg)})"
       else
-        if exist?(pkg)
+        if exist?(pkg, **options)
           "no (#{modversion(pkg)})"
         else
           "no (nonexistent)"
@@ -408,6 +410,7 @@ class PackageConfig
     @paths.unshift(*(@options[:paths] || []))
     @paths = normalize_paths(@paths)
     @msvc_syntax = @options[:msvc_syntax]
+    @static = @options[:static]
     @variables = @declarations = nil
     override_variables = self.class.custom_override_variables
     @override_variables = parse_override_variables(override_variables)
@@ -528,7 +531,12 @@ class PackageConfig
 
   private
   def collect_cflags
-    target_packages = [self, *all_required_packages]
+    target_packages = [self]
+    if @static
+      target_packages += all_required_packages
+    else
+      target_packages += required_packages
+    end
     cflags_set = []
     target_packages.each do |package|
       cflags_set << package.declaration("Cflags")
@@ -580,7 +588,12 @@ class PackageConfig
   end
 
   def collect_libs
-    target_packages = [*required_packages, self]
+    if @static
+      target_packages = required_packages
+    else
+      target_packages = all_required_packages
+    end
+    target_packages += [self]
     libs_set = []
     target_packages.each do |package|
       libs_set << package.declaration("Libs")

otegami added a commit to otegami/pkg-config that referenced this pull request Nov 27, 2025
GitHub:
- ruby-gnome#41 (comment)
- ruby-gnome#41 (comment)

By default, exclude `Requires.private` dependencies
from cflags and libs output to match pkgconf's
behavior.
When the new `static: true` option is specified,
include `Requires.private` dependencies for static
linking scenarios.

This change makes the default behavior consistent with
pkgconf while still supporting static linking
use-cases through an explicit option.

Co-Authored-By: kou <kou@clear-code.com>
@otegami
Copy link
Contributor Author

otegami commented Nov 27, 2025

@kou
Thank you so much. I've just created the PR for using Requires.private only for static linking mode.

@otegami otegami marked this pull request as draft November 28, 2025 02:11
## Issue

On MSYS2 environments, installing gems that depend on
pkgconf might fail because the pkgconf system package
cannot be automatically installed.

## Cause

The pkg-config gem was missing MSYS2 platform
configuration in its system package requirements.
Without this, the rubygems-requirements-system gem
cannot automatically install the pkgconfg system
package on MSYS2 environments.

## Solution

Add `system: pkgconf: msys2: pkgconf` to the gemspec
requirements.
This works with rubygems-requirements-system which
supports MSYS2 platform.
@otegami otegami force-pushed the support-msys2-pkgconf-package branch from 3ab9fea to 66fed8a Compare December 1, 2025 08:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants