Skip to content

Conversation

@shs96c
Copy link
Member

@shs96c shs96c commented Dec 19, 2025

User description

The Google Closure Library is the last public release, and it seems unlikely we'll ever update it again.

The new version of rules_closure brings a new version of the closure compiler. This is stricter than the previous compiler, and necessitated an update of the Google Closure Library. However, this new version of the library had removed some key functions we relied upon. We've had to rework both our own code and the vendored version of closure to get it to work with the latest closure compiler.


PR Type

Enhancement, Bug fix


Description

  • Upgrade rules_closure and Google Closure Library to the latest release

  • Modernize vendored Closure Library code to ES6 modules and classes, replacing legacy goog.provide/goog.require with goog.module syntax

  • Update license headers to SPDX format across multiple files

  • Remove deprecated utility functions from goog.base (goog.isDef, goog.isFunction, goog.isArray, etc.) and replace usages with native JavaScript equivalents

  • Add User-Agent Client Hints (UACH) support to browser detection with new Brand enum and version APIs

  • Modernize SafeHtml to ES6 class with private constructor token validation

  • Enhance testing assertions with custom equality matchers and support for Map, Set, and ArrayBuffer comparison

  • Add strict mode directives and type-safe HTML element retrieval helpers to DOM utilities

  • Migrate array utilities to ES6 modules with new bucketToMap and toMap functions

  • Remove obsolete browser compatibility code for IE8 and earlier versions

  • Add Trusted Types policy support and modernize global evaluation

  • Remove unused i18n, date/time, and legacy UI components (533+ files deleted)

  • Fix atom locators to use native typeof operator instead of deprecated goog.isFunction

  • Update error handling to use goog.utils.inherits instead of deprecated goog.inherits


Diagram Walkthrough

flowchart LR
  A["Legacy Closure Library<br/>goog.provide/require"] -->|Modernize| B["ES6 goog.module<br/>syntax"]
  C["Deprecated utilities<br/>goog.isFunction etc."] -->|Replace| D["Native JavaScript<br/>typeof, Array.isArray"]
  E["Old browser support<br/>IE8, Safari 10"] -->|Remove| F["Modern browser<br/>only code"]
  B -->|Result| G["Updated Closure Library<br/>with latest compiler"]
  D -->|Result| G
  F -->|Result| G
Loading

File Walkthrough

Relevant files
Enhancement
6 files
safehtml.js
Modernize SafeHtml to ES6 class with goog.module                 

third_party/closure/goog/html/safehtml.js

  • Converted from legacy goog.provide/goog.require to modern goog.module
    syntax with ES6 class-based implementation
  • Refactored SafeHtml from constructor function to ES6 class with
    private constructor token validation
  • Updated license header to SPDX format and modernized JSDoc annotations
  • Removed directionality tracking (dir_ field) and related methods;
    simplified type contract
  • Migrated static methods to use modern JavaScript patterns with arrow
    functions and template literals
  • Added goog.utils dependency for type checking utilities
+812/-902
browser.js
Add User-Agent Client Hints support to browser detection 

third_party/closure/goog/labs/useragent/browser.js

  • Converted from legacy goog.provide/goog.require to modern goog.module
    syntax
  • Added support for User-Agent Client Hints (UACH) API with
    useUserAgentDataBrand() function
  • Introduced Brand enum for browser brand identification and version
    checking
  • Added new high-entropy version APIs: fullVersionOf(), isAtLeast(),
    isAtMost(), getVersionStringForLogging()
  • Refactored version detection to use HighEntropyBrandVersion class for
    async version loading
  • Updated license header to SPDX format and modernized function
    signatures with default parameters
+633/-222
base.js
Modernize Closure Library base.js for latest compiler       

third_party/closure/goog/base.js

  • Updated license header to modern SPDX format
  • Removed deprecated utility functions (goog.isDef, goog.isString,
    goog.isBoolean, goog.isNumber, goog.isNull, goog.isDefAndNotNull,
    goog.isArray, goog.isFunction, goog.getHashCode, goog.removeHashCode,
    goog.mixin, goog.globalize, goog.base, goog.nullFunction)
  • Removed transpilation-related code and goog.Transpiler class
  • Removed Safari 10 workaround functions (goog.useSafari10Workaround,
    goog.workaroundSafari10EvalBug)
  • Simplified goog.typeOf implementation using modern Array.isArray
  • Updated goog.exportPath_ to support namespace merging with
    overwriteImplicit parameter
  • Changed goog.ENABLE_DEBUG_LOADER default from true to false
  • Added Trusted Types policy support with goog.createTrustedTypesPolicy
    and goog.CLOSURE_EVAL_PREFILTER_
  • Modernized goog.globalEval to use direct eval call
  • Added goog.GetMsgOptions record type and enhanced goog.getMsg with
    unescapeHtmlEntities option
  • Removed old IE detection and compatibility code
    (goog.DebugLoader_.IS_OLD_IE_)
  • Updated module loading system to remove transpilation dependencies
  • Added goog.isEdge_ function for pre-Chromium Edge detection
+355/-998
asserts.js
Enhance testing asserts with custom equality matchers       

third_party/closure/goog/testing/asserts.js

  • Updated license header to modern SPDX format
  • Renamed PRIMITIVE_EQUALITY_PREDICATES to EQUALITY_PREDICATES and added
    support for Trusted Types
  • Added PRIMITIVE_TRUE_TYPES array for type checking
  • Implemented custom equality matcher system with
    goog.testing.asserts.registerComparator and related functions
  • Added support for ArrayBuffer comparison
  • Enhanced _trueTypeOf to use modern loop-based constructor matching
  • Added goog.GetMsgOptions related requires
  • Improved error messages in assertion functions with better type
    information
  • Added adapter support for external assertion frameworks
  • Modernized string methods from substr to slice
  • Added 'use strict' directives throughout
  • Enhanced findDifferences to support custom equality functions and
    better error reporting
  • Added support for Map and Set comparison
  • Improved HTML standardization using safe DOM methods
+390/-91
array.js
Modernize array.js to ES6 modules and update syntax           

third_party/closure/goog/array/array.js

  • Migrated from legacy goog.provide/goog.require to ES6 goog.module
    syntax with proper exports
  • Updated license header to modern SPDX format
  • Converted all function declarations to use const/function with
    explicit exports statements
  • Changed variable declarations from var to const/let for better scoping
  • Added new utility functions bucketToMap and toMap for ES6 Map support
  • Updated internal function calls to use local references instead of
    goog.array namespace
+462/-346
dom.js
Add strict mode and HTML element assertion helpers to dom.js

third_party/closure/goog/dom/dom.js

  • Updated license header to modern SPDX format
  • Added 'use strict' directives to all functions for stricter JavaScript
    semantics
  • Added new helper functions getHTMLElement, getRequiredHTMLElement,
    getHTMLElementByClass, getRequiredHTMLElementByClass for type-safe
    element retrieval
  • Added new copyContents function to safely replace child nodes without
    using innerHTML
  • Removed obsolete browser compatibility checks for IE8 and earlier
    versions
  • Updated internal references to use goog.utils and goog.asserts.dom
    modules
  • Changed goog.isArray to Array.isArray and goog.isArrayLike to
    goog.utils.isArrayLike
+278/-105
Bug fix
2 files
locators.js
Remove deprecated goog.isFunction usage                                   

javascript/atoms/locators/locators.js

  • Added missing goog.require statements for bot.Error and bot.ErrorCode
  • Replaced deprecated goog.isFunction() calls with native typeof
    operator checks
+4/-2     
error.js
Update error.js to use goog.utils.inherits                             

javascript/atoms/error.js

  • Added goog.require('goog.utils') dependency
  • Changed goog.inherits call to goog.utils.inherits for inheritance
    setup
+3/-1     
Configuration changes
1 files
BUILD.bazel
Add Bazel build configuration for labs package                     

third_party/closure/goog/labs/BUILD.bazel

  • Created new Bazel build configuration file for the labs package
  • Set default visibility to public for all targets
  • Added notice license declaration
+3/-0     
Additional files
101 files
.bazelrc +2/-2     
MODULE.bazel +7/-3     
WORKSPACE +0/-21   
maven_install.json +647/-2 
BUILD.bazel +0/-1     
BUILD.bazel +17/-9   
action.js +11/-9   
device.js +19/-17 
dom.js +4/-4     
domcore.js +1/-1     
events.js +29/-14 
html5_browser.js +9/-9     
location.js +2/-0     
inject.js +19/-17 
keyboard.js +121/-12
css.js +5/-4     
link_text.js +1/-0     
relative.js +7/-4     
xpath.js +2/-1     
mouse.js +13/-13 
response.js +4/-3     
click_link_test.html +2/-2     
click_test.html +3/-3     
storage_test.html +1/-1     
inject_test.html +3/-3     
property_test.html +3/-3     
test_bootstrap.js +3/-0     
type_test.html +4/-4     
touchscreen.js +7/-5     
window.js +3/-3     
BUILD.bazel +1/-1     
test_bootstrap.js +5/-0     
BUILD.bazel +1/-1     
test_bootstrap.js +5/-0     
BUILD.bazel +4/-1     
closure_js_deps.bzl +4/-1     
fragment.bzl +1/-1     
BUILD.bazel +1/-1     
BUILD.bazel +4/-3     
attribute.js +6/-5     
element.js +1/-1     
BUILD.bazel +1/-3     
test_bootstrap.js +5/-0     
COPYING +0/-202 
README.md +0/-501 
BUILD.bazel +58/-67 
BUILD.bazel +5/-33   
announcer.js +0/-121 
aria.js +28/-17 
attributes.js +10/-13 
datatables.js +32/-34 
roles.js +5/-13   
BUILD.bazel +5/-9     
BUILD.bazel +14/-8   
asserts.js +121/-133
dom.js +289/-0 
BUILD.bazel +16/-54 
animationdelay.js +18/-15 
conditionaldelay.js +0/-226 
debouncer.js +0/-206 
delay.js +14/-24 
freelist.js +15/-28 
nexttick.js +30/-64 
run.js +67/-81 
throttle.js +0/-198 
throwexception.js +28/-0   
workqueue.js +92/-109
BUILD.bazel +17/-0   
iters.js +232/-0 
maps.js +159/-0 
BUILD.bazel +2/-20   
alpha.js +0/-501 
color.js +100/-68
names.js +5/-13   
conformance_proto.txt +420/-0 
BUILD.bazel +10/-0   
autocomplete.css +41/-0   
bubble.css +86/-0   
button.css +41/-0   
charpicker.css +205/-0 
checkbox.css +38/-0   
colormenubutton.css +5/-2     
colorpalette.css +3/-3     
colorpicker-simplegrid.css +49/-0   
combobox.css +57/-0   
common.css +41/-0   
css3button.css +78/-0   
css3menubutton.css +22/-0   
custombutton.css +167/-0 
datepicker.css +154/-0 
dialog.css +70/-0   
dimensionpicker.css +46/-0   
dragdropdetector.css +50/-0   
BUILD.bazel +1/-0     
editortoolbar.css +2/-3     
filteredmenu.css +31/-0   
filterobservingmenuitem.css +25/-0   
flatbutton.css +66/-0   
flatmenubutton.css +63/-0   
hovercard.css +51/-0   
Additional files not shown

The Google Closure Library is the last public release, and it seems
unlikely we'll ever update it again.

The new version of `rules_closure` brings a new version of the closure
compiler. This is stricter than the previous compiler, and necessitated
an update of the Google Closure Library. However, this new version of
the library had removed some key functions we relied upon. We've had
to rework both our own code and the vendored version of closure to get
it to work with the latest closure compiler.
@selenium-ci selenium-ci added C-java Java Bindings B-atoms JavaScript chunks generated by Google closure B-build Includes scripting, bazel and CI integrations labels Dec 19, 2025
@qodo-code-review
Copy link
Contributor

qodo-code-review bot commented Dec 19, 2025

PR Compliance Guide 🔍

Below is a summary of compliance checks for this PR:

Security Compliance
Dynamic code execution

Description: The PR introduces/expands dynamic code execution and script injection primitives in
base.js (e.g., goog.globalEval now unconditionally calls (0, eval)(script) and
debug-loader paths build and doc.write() <script> tags, while a permissive Trusted Types policy is
created via goog.createTrustedTypesPolicy that blesses arbitrary strings), which can
become an XSS/code-execution vector if any caller ever passes attacker-controlled content
or paths into these APIs.
base.js [1470-3550]

Referred Code
 * Evals JavaScript in the global scope.
 *
 * Throws an exception if neither execScript or eval is defined.
 * @param {string|!TrustedScript} script JavaScript string.
 */
goog.globalEval = function(script) {
  (0, eval)(script);
};


/**
 * Optional map of CSS class names to obfuscated names used with
 * goog.getCssName().



 ... (clipped 2060 lines)
Ticket Compliance
🎫 No ticket provided
  • Create ticket/issue
Codebase Duplication Compliance
Codebase context is not defined

Follow the guide to enable codebase context checks.

Custom Compliance
🟢
Generic: Comprehensive Audit Trails

Objective: To create a detailed and reliable record of critical system actions for security analysis
and compliance.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Meaningful Naming and Self-Documenting Code

Objective: Ensure all identifiers clearly express their purpose and intent, making code
self-documenting

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Security-First Input Validation and Data Handling

Objective: Ensure all data inputs are validated, sanitized, and handled securely to prevent
vulnerabilities

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

🔴
Generic: Robust Error Handling and Edge Case Management

Objective: Ensure comprehensive error handling that provides meaningful context and graceful
degradation

Status:
Silent dependency failure: The debug loader no longer throws when a required namespace cannot be found, which can
allow execution to continue in a broken state with only a console log.

Referred Code
goog.DebugLoader_.prototype.load_ = function(namespace) {
  if (!this.getPathFromDeps_(namespace)) {
    var errorMessage = 'goog.require could not find: ' + namespace;
    goog.logToConsole_(errorMessage);
  } else {

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Error Handling

Objective: To prevent the leakage of sensitive system information through error messages while
providing sufficient detail for internal debugging.

Status:
Raw exception message: The Trusted Types policy creation catch block logs the raw exception message, which may
expose internal details depending on browser/runtime behavior.

Referred Code
try {
  policy = policyFactory.createPolicy(name, {
    createHTML: goog.identity_,
    createScript: goog.identity_,
    createScriptURL: goog.identity_
  });
} catch (e) {
  goog.logToConsole_(e.message);
}

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Logging Practices

Objective: To ensure logs are useful for debugging and auditing without exposing sensitive
information like PII, PHI, or cardholder data.

Status:
Unvetted console logging: The code logs e.message from a caught exception via goog.logToConsole_, which could
inadvertently include sensitive data depending on the thrown error content.

Referred Code
try {
  policy = policyFactory.createPolicy(name, {
    createHTML: goog.identity_,
    createScript: goog.identity_,
    createScriptURL: goog.identity_
  });
} catch (e) {
  goog.logToConsole_(e.message);
}

Learn more about managing compliance generic rules or creating your own custom rules

  • Update
Compliance status legend 🟢 - Fully Compliant
🟡 - Partial Compliant
🔴 - Not Compliant
⚪ - Requires Further Human Verification
🏷️ - Compliance label

@qodo-code-review
Copy link
Contributor

qodo-code-review bot commented Dec 19, 2025

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
High-level
Avoid manually forking the Closure library

Instead of directly modifying the vendored Closure Library source code, manage
the required changes as a set of patch files. This approach separates
project-specific modifications from the official library, simplifying future
updates and maintenance.

Examples:

third_party/closure/goog/base.js [1-3551]
third_party/closure/goog/html/safehtml.js [1-1002]

Solution Walkthrough:

Before:

// The PR's approach: direct modification of third-party code.
// This makes it hard to track changes or apply upstream updates.

project/
└── third_party/
    └── closure/
        └── goog/
            ├── base.js           // <-- Heavily modified in-place
            ├── array/
               └── array.js      // <-- Heavily modified in-place
            └── html/
                └── safehtml.js   // <-- Heavily modified in-place

After:

// The suggested approach: keep original library + apply patches.
// This isolates custom changes and simplifies future maintenance.

project/
├── patches/
   ├── 001-modernize-modules.patch
   └── 002-custom-compatibility-fixes.patch
└── third_party/
    └── closure/                // <-- Pristine, official release
        └── goog/
            ├── base.js
            ├── array/
               └── array.js
            └── html/
                └── safehtml.js

// Build process:
// 1. Start with official Closure library.
// 2. Apply all files from `patches/`.
// 3. Continue build.
Suggestion importance[1-10]: 10

__

Why: This suggestion correctly identifies a critical architectural issue, as directly modifying vendored dependencies like the Closure Library creates a significant long-term maintenance burden, making future upgrades or security patches extremely difficult.

High
Possible issue
Validate disallowed tags correctly

Change tagName.toUpperCase() to tagName.toLowerCase() in verifyTagName to
correctly check against the NOT_ALLOWED_TAG_NAMES set.

third_party/closure/goog/html/safehtml.js [343]

-if (tagName.toUpperCase() in NOT_ALLOWED_TAG_NAMES) {
+if (tagName.toLowerCase() in NOT_ALLOWED_TAG_NAMES) {

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 8

__

Why: This is a critical bug fix, as the existing code uses toUpperCase() to check against a set of lower-case tag names, failing to block disallowed tags.

Medium
Replace recursion with an iterative approach

Replace the recursive implementation of the flatten function with an iterative,
stack-based approach to prevent potential stack overflow errors with large
nested arrays.

third_party/closure/goog/array/array.js [1604-1623]

 function flatten(var_args) {
-  const CHUNK_SIZE = 8192;
-
   const result = [];
-  for (let i = 0; i < arguments.length; i++) {
-    const element = arguments[i];
+  const stack = [...arguments];
+  while (stack.length) {
+    const element = stack.pop();
     if (Array.isArray(element)) {
-      for (let c = 0; c < element.length; c += CHUNK_SIZE) {
-        const chunk = slice(element, c, c + CHUNK_SIZE);
-        const recurseResult = flatten.apply(null, chunk);
-        for (let r = 0; r < recurseResult.length; r++) {
-          result.push(recurseResult[r]);
-        }
+      // The items of the array are pushed in reverse order so that they are
+      // processed in their original order.
+      for (let i = element.length - 1; i >= 0; i--) {
+        stack.push(element[i]);
       }
     } else {
-      result.push(element);
+      result.unshift(element);
     }
   }
   return result;
 }

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 8

__

Why: The suggestion correctly identifies a potential stack overflow issue with the recursive implementation of flatten and proposes a more robust iterative solution, which is a critical improvement for correctness and performance.

Medium
Guard against undefined attributes

Change the default value of the attributes parameter in createScript to an empty
object ({}) to prevent runtime errors.

third_party/closure/goog/html/safehtml.js [517]

-static createScript(script, attributes = undefined) {
+static createScript(script, attributes = {}) {

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 7

__

Why: This is a good suggestion that prevents a TypeError when createScript is called without attributes by providing a safe default value, improving the function's robustness.

Medium
Use a safer hasOwnProperty check

Replace object.hasOwnProperty(prop) with the safer
Object.prototype.hasOwnProperty.call(object, prop) to prevent potential errors
when iterating over object properties.

third_party/closure/goog/base.js [135-145]

 if (!overwriteImplicit && goog.isObject(object) &&
     goog.isObject(cur[part])) {
   // Merge properties on object (the input parameter) with the existing
   // implicitly defined namespace, so as to not clobber previously
   // defined child namespaces.
   for (var prop in object) {
-    if (object.hasOwnProperty(prop)) {
+    if (Object.prototype.hasOwnProperty.call(object, prop)) {
       cur[part][prop] = object[prop];
     }
   }
 } else {

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 6

__

Why: The suggestion correctly identifies a potential robustness issue with using object.hasOwnProperty directly and proposes a safer, standard alternative, which is a valuable improvement for code quality.

Low
General
Support opt_obj in bucketToMap

Add an optional opt_obj parameter to bucketToMap to allow callers to specify a
this context for the sorter function, matching the behavior of the bucket
function.

third_party/closure/goog/array/array.js [1455-1473]

-function bucketToMap(array, sorter) {
-  const /** !Map<K, !Array<V>> */ buckets = new Map();
+function bucketToMap(array, sorter, opt_obj) {
+  const buckets = new Map();
   for (let i = 0; i < array.length; i++) {
     const value = array[i];
-    const key = sorter(value, i, array);
+    const key = sorter.call(opt_obj, value, i, array);
     if (key !== undefined) {
       let bucket = buckets.get(key);
       if (!bucket) {
         bucket = [];
         buckets.set(key, bucket);
       }
       bucket.push(value);
     }
   }
   return buckets;
 }

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 7

__

Why: The suggestion correctly points out an API inconsistency between the new bucketToMap function and the existing bucket function, improving API consistency and preventing potential misuse.

Medium
Ensure consistent attribute serialization order

Sort attribute keys alphabetically in stringifyAttributes before iteration to
ensure a consistent and deterministic order of attributes in the generated HTML.

third_party/closure/goog/html/safehtml.js [727-748]

 +  static stringifyAttributes(tagName, attributes = undefined) {
 +    let result = '';
 +    if (attributes) {
-+      for (let name in attributes) {
++      const keys = Object.keys(attributes).sort();
++      for (const name of keys) {
 +        // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty#Using_hasOwnProperty_as_a_property_name
 +        if (Object.prototype.hasOwnProperty.call(attributes, name)) {
 +          if (!VALID_NAMES_IN_TAG.test(name)) {
 +            throw new Error(
 +                SafeHtml.ENABLE_ERROR_MESSAGES ?
 +                    `Invalid attribute name "${name}".` :
 +                    '');
 +          }
 +          const value = attributes[name];
 +          if (value == null) {
 +            continue;
 +          }
 +          result += ' ' + getAttrNameAndValue(tagName, name, value);
 +        }
 +      }
 +    }
 +    return result;
 +  }

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 5

__

Why: The suggestion correctly points out that object key order is not guaranteed and proposes sorting to ensure deterministic output, which improves testability and consistency.

Low
Learned
best practice
Prefilter code before eval

Route script through the existing Trusted Types prefilter (and keep the raw eval
call) so untrusted strings are not evaluated directly.

third_party/closure/goog/base.js [1475-1477]

 goog.globalEval = function(script) {
-  (0, eval)(script);
+  (0, eval)(goog.CLOSURE_EVAL_PREFILTER_.createScript(script));
 };

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 6

__

Why:
Relevant best practice - Validate and sanitize external inputs before executing code (avoid raw eval).

Low
Clamp and normalize timeout values

Normalize secs to a finite, non-negative integer (e.g., clamp at 0) to avoid
producing invalid refresh directives when callers pass negative/NaN/float
values.

third_party/closure/goog/html/safehtml.js [589-621]

 static createMetaRefresh(url, secs = undefined) {
   // Note that sanitize is a no-op on SafeUrl.
   let unwrappedUrl = SafeUrl.unwrap(SafeUrl.sanitize(url));
 ...
+  const safeSecs = Number.isFinite(secs) ? Math.max(0, Math.floor(secs)) : 0;
   const attributes = {
     'http-equiv': 'refresh',
-    'content': (secs || 0) + '; url=' + unwrappedUrl,
+    'content': safeSecs + '; url=' + unwrappedUrl,
   };
 ...
 }

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 5

__

Why:
Relevant best practice - Guard numeric conversions and clamp time values to safe bounds before use.

Low
  • Update

@titusfortner titusfortner merged commit c371cd4 into SeleniumHQ:trunk Dec 20, 2025
40 of 42 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

B-atoms JavaScript chunks generated by Google closure B-build Includes scripting, bazel and CI integrations C-java Java Bindings Review effort 4/5

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants