From 20b145798df30cf340073561a49b944050e12a57 Mon Sep 17 00:00:00 2001
From: Karsten Sperling <113487422+ksperling-apple@users.noreply.github.com>
Date: Thu, 30 May 2024 03:52:11 +1200
Subject: [PATCH] Linux: Don't fail if glib is missing if we don't need it
 (#33647)

This adds support for an `optional` property to the `pkg_config` template.
---
 build/chip/linux/BUILD.gn         |  3 +-
 build/config/linux/pkg-config.py  | 11 ++++--
 build/config/linux/pkg_config.gni | 56 ++++++++++++++++++-------------
 3 files changed, 43 insertions(+), 27 deletions(-)

diff --git a/build/chip/linux/BUILD.gn b/build/chip/linux/BUILD.gn
index c86f803a70f3d3..a530d5a611a25e 100644
--- a/build/chip/linux/BUILD.gn
+++ b/build/chip/linux/BUILD.gn
@@ -1,4 +1,4 @@
-# Copyright (c) 2020 Project CHIP Authors
+# Copyright (c) 2020-2024 Project CHIP Authors
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -21,4 +21,5 @@ pkg_config("glib") {
     "glib-2.0",
     "gio-unix-2.0",
   ]
+  optional = true  # Only certain conditionally-compiled modules depend on glib
 }
diff --git a/build/config/linux/pkg-config.py b/build/config/linux/pkg-config.py
index 4d6e773a00d666..f44667242de807 100755
--- a/build/config/linux/pkg-config.py
+++ b/build/config/linux/pkg-config.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python
 # Copyright (c) 2013 The Chromium Authors. All rights reserved.
-# Copyright (c) 2020 Project CHIP Authors
+# Copyright (c) 2020-2024 Project CHIP Authors
 #
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions are
@@ -127,6 +127,7 @@ def RewritePath(path, strip_prefix, sysroot):
 def main():
     parser = OptionParser()
     parser.add_option('-d', '--debug', action='store_true')
+    parser.add_option('-o', '--optional', action='store_true')
     parser.add_option('-p', action='store', dest='pkg_config', type='string',
                       default='pkg-config')
     parser.add_option('-v', action='append', dest='strip_out', type='string')
@@ -209,6 +210,10 @@ def main():
     try:
         flag_string = subprocess.check_output(cmd).decode('utf-8')
     except Exception:
+        if options.optional:
+            sys.stderr.write('Ignoring failure to run pkg-config for optional library.\n')
+            print(json.dumps([False]))  # Output a GN array indicating missing optional packages
+            return 0
         sys.stderr.write('Could not run pkg-config.\n')
         return 1
 
@@ -248,10 +253,10 @@ def main():
         else:
             cflags.append(flag)
 
-    # Output a GN array, the first one is the cflags, the second are the libs. The
+    # Output a GN array, indicating success and our output lists.
     # JSON formatter prints GN compatible lists when everything is a list of
     # strings.
-    print(json.dumps([includes, cflags, libs, lib_dirs]))
+    print(json.dumps([True, includes, cflags, libs, lib_dirs]))
     return 0
 
 
diff --git a/build/config/linux/pkg_config.gni b/build/config/linux/pkg_config.gni
index 016defafbc3617..d6892d97fb976f 100644
--- a/build/config/linux/pkg_config.gni
+++ b/build/config/linux/pkg_config.gni
@@ -1,5 +1,5 @@
 # Copyright (c) 2013 The Chromium Authors. All rights reserved.
-# Copyright (c) 2020 Project CHIP Authors
+# Copyright (c) 2020-2024 Project CHIP Authors
 #
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions are
@@ -43,9 +43,14 @@
 #
 # You can also use "extra args" to filter out results (see pkg-config.py):
 #   extra_args = [ "-v, "foo" ]
+#
 # To ignore libs and ldflags (only cflags/defines will be set, which is useful
 # when doing manual dynamic linking), set:
 #   ignore_libs = true
+#
+# To allow the build to proceed if (any of) the requested packages are absent, set:
+#   optional = true
+# In this case the resulting config object will be empty.
 
 import("//build_overrides/build.gni")
 import("${build_root}/config/sysroot.gni")
@@ -109,37 +114,42 @@ template("pkg_config") {
     } else {
       args = pkg_config_args + invoker.packages
     }
+    if (defined(invoker.optional) && invoker.optional) {
+      args += [ "-o" ]
+    }
     if (defined(invoker.extra_args)) {
       args += invoker.extra_args
     }
 
+    # pkgresult = [present, includes, cflags, libs, lib_dirs]
     pkgresult = exec_script(pkg_config_script, args, "value")
-    cflags = pkgresult[1]
+    if (pkgresult[0]) {
+      cflags = pkgresult[2]
 
-    foreach(include, pkgresult[0]) {
-      cflags += [ "-I$include" ]
-    }
+      foreach(include, pkgresult[1]) {
+        cflags += [ "-I$include" ]
+      }
 
-    if (!defined(invoker.ignore_libs) || !invoker.ignore_libs) {
-      libs = pkgresult[2]
-      lib_dirs = pkgresult[3]
-    }
+      if (!defined(invoker.ignore_libs) || !invoker.ignore_libs) {
+        libs = pkgresult[3]
+        lib_dirs = pkgresult[4]
+      }
 
-    # Link libraries statically for OSS-Fuzz fuzzer build
-    if (oss_fuzz) {
-      libs = []
-      ldflags = [ "-Wl,-Bstatic" ]
-      foreach(lib, pkgresult[2]) {
-        ldflags += [ "-l$lib" ]
+      # Link libraries statically for OSS-Fuzz fuzzer build
+      if (oss_fuzz) {
+        libs = []
+        ldflags = [ "-Wl,-Bstatic" ]
+        foreach(lib, pkgresult[3]) {
+          ldflags += [ "-l$lib" ]
+        }
+        ldflags += [ "-Wl,-Bdynamic" ]
+        lib_dirs = pkgresult[4]
       }
-      ldflags += [ "-Wl,-Bdynamic" ]
-      lib_dirs = pkgresult[3]
-    }
 
-    forward_variables_from(invoker,
-                           [
-                             "defines",
-                             "visibility",
-                           ])
+      forward_variables_from(invoker, [ "defines" ])
+    }
   }
+
+  # Always forward visibility
+  forward_variables_from(invoker, [ "visibility" ])
 }