Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
f51a5f9
Revert "feat(swift): support included_ids in package and module confi…
JoeWang1127 Aug 14, 2026
27c21e3
feat(internal/sidekick/rust): wrap stream closed errors in BrokenPipe…
suzmue Aug 14, 2026
231308c
fix(.github): install protoc in rust integration (#7290)
JoeWang1127 Aug 14, 2026
54acc44
feat(sidekick/rust): skip convert for Any fields (#7292)
coryan Aug 14, 2026
f8beaf0
feat(internal/sidekick/rust): support request headers for bidi stream…
suzmue Aug 14, 2026
81af4a7
cleanup(sidekick): silence warning (#7276)
coryan Aug 14, 2026
1945661
test(java): run integration test on PR with fine-grained benchmark ti…
yangyzs Aug 15, 2026
ff70d99
feat(internal/librarian/java): add universal Nailgun JVM daemon and a…
yangyzs Aug 15, 2026
18055fb
ci(java): invalidate stale librarian tools cache and hash tool source…
yangyzs Aug 15, 2026
154f599
feat(internal/librarian/java): parallelize google-java-format batching
yangyzs Aug 15, 2026
4b9c27f
feat(internal/librarian): parallelize java.Generate across libraries
yangyzs Aug 15, 2026
b3f2148
fix(internal/librarian/java): fix NGServer arguments and enable Nailg…
yangyzs Aug 15, 2026
1bc3664
fix(internal/librarian/java): add JDK compiler export flags to NGServ…
yangyzs Aug 15, 2026
23bd4d8
fix(internal/tool/maven): add JDK export flags to wrapper script fall…
yangyzs Aug 15, 2026
3c3484a
fix(internal/tool/maven): implement complete Nailgun protocol in wrap…
yangyzs Aug 16, 2026
6d3ee23
perf(internal/librarian): optimize Java generation concurrency and fo…
yangyzs Aug 16, 2026
a2dad8c
perf(internal/librarian): inline per-library formatting and cap worke…
yangyzs Aug 16, 2026
aa2da26
perf(internal/librarian): speed up Java generation phase with 4 worke…
yangyzs Aug 16, 2026
ae1d67a
perf(internal/librarian/java): optimize file collection to format onl…
yangyzs Aug 16, 2026
188a51c
fix(internal/librarian/java): resolve git status relative paths again…
yangyzs Aug 16, 2026
73eebc5
perf(internal/librarian/java): include all lib directory jars in Nail…
yangyzs Aug 16, 2026
5b438fd
perf(internal/librarian/java): format Java libraries inline during ge…
yangyzs Aug 16, 2026
48ce77d
fix(internal/librarian/java): forward NAILGUN_PORT in getToolsEnv to …
yangyzs Aug 16, 2026
67a2411
perf(internal/librarian/java): tune format batch size and concurrency…
yangyzs Aug 16, 2026
b654d0f
perf(internal/librarian/java): cap formatting worker limit to prevent…
yangyzs Aug 16, 2026
229a155
perf(internal/librarian/java): expand Nailgun JVM heap to 5G and form…
yangyzs Aug 16, 2026
72466e8
perf(internal/librarian/java): set safe -Xmx3584m daemon heap and 2-w…
yangyzs Aug 16, 2026
7ec9294
perf(internal/librarian/java): set 300-file format batch size and 4-w…
yangyzs Aug 17, 2026
fea2b19
perf(internal/librarian/java): limit formatting concurrency to runtim…
yangyzs Aug 17, 2026
81884a6
perf(internal/librarian/java): combine proto, grpc, and gapic protoc …
yangyzs Aug 17, 2026
270779e
perf(internal/librarian/java): tune batch formatting size to 150 file…
yangyzs Aug 17, 2026
9cc217a
perf(internal/librarian/java): set optimal 1000-file batch size and r…
yangyzs Aug 17, 2026
0a16581
perf(internal/librarian/java): run multi-daemon JVM pool on separate …
yangyzs Aug 17, 2026
735707c
perf(internal/librarian/java): skip redundant re-formatting of protoc…
yangyzs Aug 17, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/java.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ jobs:
with:
path: |
~/.cache/librarian
key: librarian-java-tools-v2-${{ runner.os }}-${{ hashFiles('internal/config/**/*.go') }}-${{ hashFiles('google-cloud-java/librarian.yaml', 'google-cloud-java/sdk-platform-java/gapic-generator-java/**') }}
key: librarian-java-tools-v3-${{ runner.os }}-${{ hashFiles('internal/config/**/*.go', 'internal/tool/maven/**/*.go', 'internal/librarian/java/**/*.go') }}-${{ hashFiles('google-cloud-java/librarian.yaml', 'google-cloud-java/sdk-platform-java/gapic-generator-java/**') }}
- name: Verify Java and Maven installation
if: steps.cache-tools.outputs.cache-hit != 'true'
run: |
Expand Down Expand Up @@ -116,7 +116,7 @@ jobs:
working-directory: google-cloud-java
run: librarian generate secretmanager
- name: Run librarian generate all (integration test)
if: matrix.task == 'integration' && github.event_name == 'push' && github.ref == 'refs/heads/main'
if: matrix.task == 'integration'
working-directory: google-cloud-java
run: librarian generate --all
create-issue-on-failure:
Expand Down
8 changes: 5 additions & 3 deletions .github/workflows/sidekick.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ jobs:
- name: Display Go version
run: go version
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@4cda84d5c5c54efe2404f9d843567869ab1699d4 # stable
uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable
- name: Display Cargo version
run: cargo version
- name: Display rustc version
Expand All @@ -49,14 +49,16 @@ jobs:
persist-credentials: false
- uses: ./.github/actions/setup-librarian
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@4cda84d5c5c54efe2404f9d843567869ab1699d4 # stable
- uses: ./.github/actions/install-taplo
uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable
- name: Checkout google-cloud-rust
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
repository: googleapis/google-cloud-rust
path: google-cloud-rust
persist-credentials: false
- name: Run librarian install
working-directory: google-cloud-rust
run: librarian install -v
- name: Run librarian generate
working-directory: google-cloud-rust
run: librarian generate --all
Expand Down
2 changes: 0 additions & 2 deletions doc/config-schema.md
Original file line number Diff line number Diff line change
Expand Up @@ -642,7 +642,6 @@ This document describes the schema for the librarian.yaml.
| `api_path` | string | Is the proto path to generate from (e.g., "google/storage/v2"). |
| `module_type` | string | Is the type of module to generate (e.g., "swift-protobuf", "convert-swift", or empty/"default" for standard GAPIC). |
| `include_list` | list of string | Is a subset of proto files under the target API path to include. This is typically reserved for special cases to avoid generating unused/dead code. For example, in Storage we need Protobuf gencode for a subset of the protos in the google/type directory. This code is private to the package (google-cloud-storage in Rust, GoogleCloudStorage in Swift). All other files in google/type would be dead code. |
| `included_ids` | list of string | Is a list of proto IDs to include in generation for this module. If set, only these IDs and their dependencies are generated. |
| `skipped_ids` | list of string | Is a list of proto IDs to skip in generation for this module. |
| `module_path` | string | Is the module import path or target containing stubs (used by convert-swift and gRPC transports). |

Expand All @@ -653,7 +652,6 @@ This document describes the schema for the librarian.yaml.
| (embedded) | [SwiftDefault](#swiftdefault-configuration) | |
| `library_name_override` | string | Overrides the default library name.<br><br>In Swift, each GAPIC package consists of a single product (the library), which contains a single target and module name. For example, the package for the google/cloud/secretmanager/v1 API is called google-cloud-secretmanager-v1, and contains a single product: `GoogleCloudSecretManagerV1`, which in turn contains a single target and module of the same name.<br><br>To use the library applications use this import:<br><br>``` import GoogleCloudSecretManagerV1 ```<br><br>Normally the name is derived from:<br>- If the Protobuf namespace overrides for PHP, Ruby, and C# are consistent, sidekick uses this name.<br>- Otherwise, the name implied by the Protobuf package<br>- Or the package set in the service config yaml file |
| `include_list` | list of string | Is a subset of proto files under the target API path to include (e.g., ["date.proto", "expr.proto"]). |
| `included_ids` | list of string | Is a list of proto IDs to include in generation for the package. If set, only these IDs and their dependencies are generated. |
| `skipped_ids` | list of string | Is a list of proto IDs to skip in generation for the package. |
| `modules` | list of [SwiftModule](#swiftmodule-configuration) (optional) | Specifies generation targets for veneers and test packages.<br><br>Each module defines a source proto path, and output location. |
| `package_name_override` | string | Overrides the package name.<br><br>This may be useful if the protobuf package lacks the necessary prefixes, e.g. `grafeas.v1` may be published as `google-grafeas-v1` to match the other packages. |
Expand Down
8 changes: 0 additions & 8 deletions internal/config/swift.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,6 @@ type SwiftPackage struct {
// include (e.g., ["date.proto", "expr.proto"]).
IncludeList []string `yaml:"include_list,omitempty"`

// IncludedIDs is a list of proto IDs to include in generation for the package.
// If set, only these IDs and their dependencies are generated.
IncludedIDs []string `yaml:"included_ids,omitempty"`

// SkippedIds is a list of proto IDs to skip in generation for the package.
SkippedIds []string `yaml:"skipped_ids,omitempty"`

Expand Down Expand Up @@ -162,10 +158,6 @@ type SwiftModule struct {
// in Swift). All other files in google/type would be dead code.
IncludeList []string `yaml:"include_list,omitempty"`

// IncludedIDs is a list of proto IDs to include in generation for this module.
// If set, only these IDs and their dependencies are generated.
IncludedIDs []string `yaml:"included_ids,omitempty"`

// SkippedIds is a list of proto IDs to skip in generation for this module.
SkippedIds []string `yaml:"skipped_ids,omitempty"`

Expand Down
35 changes: 31 additions & 4 deletions internal/librarian/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"fmt"
"runtime"
"strings"
"time"

"github.com/googleapis/librarian/internal/config"
"github.com/googleapis/librarian/internal/librarian/dart"
Expand Down Expand Up @@ -241,15 +242,41 @@ func generateLibraries(ctx context.Context, cfg *config.Config, libraries []*con
}
return g.Wait()
case config.LanguageJava:
daemon, err := java.StartDaemonIfConfigured(ctx, cfg)
if err == nil && daemon != nil {
defer daemon.Stop()
}
genStart := time.Now()
g, gctx := errgroup.WithContext(ctx)
g.SetLimit(max(runtime.NumCPU()*2, 4))
for _, library := range libraries {
if err := java.Generate(ctx, cfg, library, src); err != nil {
return fmt.Errorf("generate library %q (%s): %w", library.Name, cfg.Language, err)
}
g.Go(func() error {
if err := java.Generate(gctx, cfg, library, src); err != nil {
return fmt.Errorf("generate library %q (%s): %w", library.Name, cfg.Language, err)
}
return nil
})
}
if err := g.Wait(); err != nil {
return err
}
durGen := time.Since(genStart)
fmt.Printf("[BENCHMARK-CI] Phase 1: Java Code Generation Step Completed: %v\n", durGen)

fmtStart := time.Now()
if err := java.Format(ctx, libraries...); err != nil {
return fmt.Errorf("format java libraries (%s): %w", cfg.Language, err)
}
return java.PostGenerate(ctx, ".", cfg)
durFmt := time.Since(fmtStart)
fmt.Printf("[BENCHMARK-CI] Phase 2: Java Code Formatting Step Completed: %v\n", durFmt)

postStart := time.Now()
if err := java.PostGenerate(ctx, ".", cfg); err != nil {
return err
}
durPost := time.Since(postStart)
fmt.Printf("[BENCHMARK-CI] Phase 3: Java Post-Generate Step Completed: %v\n", durPost)
return nil
case config.LanguageNodejs:
g, gctx := errgroup.WithContext(ctx)
g.SetLimit(runtime.NumCPU())
Expand Down
168 changes: 168 additions & 0 deletions internal/librarian/java/daemon.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
// Copyright 2026 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package java

import (
"context"
"fmt"
"net"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
"sync"
"time"

"github.com/googleapis/librarian/internal/config"
)

var (
activeDaemonPorts []int
activeDaemonMu sync.RWMutex
)

// GetDaemonPorts returns the slice of active Nailgun daemon ports.
func GetDaemonPorts() []int {
activeDaemonMu.RLock()
defer activeDaemonMu.RUnlock()
if len(activeDaemonPorts) > 0 {
ports := make([]int, len(activeDaemonPorts))
copy(ports, activeDaemonPorts)
return ports
}
if portStr := os.Getenv("NAILGUN_PORT"); portStr != "" {
var p int
if _, err := fmt.Sscanf(portStr, "%d", &p); err == nil && p > 0 {
return []int{p}
}
}
return nil
}

// GAPICDaemon manages long-running background JVM daemon processes using Nailgun.
type GAPICDaemon struct {
cmds []*exec.Cmd
Port int
}

// StartGAPICDaemon starts a background JVM daemon process running com.martiansoftware.nailgun.NGServer.
func StartGAPICDaemon(ctx context.Context, toolsEnv map[string]string, classpath string, port int) (*GAPICDaemon, error) {
cmd := exec.CommandContext(ctx, "java",
"-Xms384m",
"-Xmx1792m",
"-XX:+UseG1GC",
"--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED",
"--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED",
"--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED",
"--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED",
"--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED",
"--add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED",
"--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED",
"-cp", classpath,
"com.martiansoftware.nailgun.NGServer",
"127.0.0.1",
fmt.Sprintf("%d", port),
)
cmd.Env = os.Environ()
for k, v := range toolsEnv {
cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", k, v))
}
if err := cmd.Start(); err != nil {
return nil, fmt.Errorf("failed to start nailgun daemon: %w", err)
}

// Wait for socket port to become ready
address := fmt.Sprintf("127.0.0.1:%d", port)
for i := 0; i < 50; i++ {

Check failure on line 89 in internal/librarian/java/daemon.go

View workflow job for this annotation

GitHub Actions / golangci-lint

rangeint: for loop can be modernized using range over int (modernize)

Check failure on line 89 in internal/librarian/java/daemon.go

View workflow job for this annotation

GitHub Actions / golangci-lint

rangeint: for loop can be modernized using range over int (modernize)
conn, err := net.DialTimeout("tcp", address, 100*time.Millisecond)
if err == nil {
conn.Close()
return &GAPICDaemon{cmds: []*exec.Cmd{cmd}, Port: port}, nil
}
time.Sleep(100 * time.Millisecond)
}

_ = cmd.Process.Kill()
return nil, fmt.Errorf("nailgun daemon failed to respond on port %d within 5 seconds", port)
}

// Stop terminates the background JVM daemon processes.
func (d *GAPICDaemon) Stop() error {
os.Unsetenv("NAILGUN_PORT")
activeDaemonMu.Lock()
activeDaemonPorts = nil
activeDaemonMu.Unlock()
if d != nil {
for _, cmd := range d.cmds {
if cmd != nil && cmd.Process != nil {
_ = cmd.Process.Kill()
}
}
}
return nil
}

// StartDaemonIfConfigured attempts to start GAPIC JVM daemons if configured tools are present.
func StartDaemonIfConfigured(ctx context.Context, cfg *config.Config) (*GAPICDaemon, error) {
if cfg == nil || cfg.Tools == nil {
return nil, nil
}
env, err := getToolsEnv()
if err != nil {
return nil, err
}
libDir, err := getLibDir()
if err != nil {
return nil, err
}
nailgunMatches, _ := filepath.Glob(filepath.Join(libDir, "nailgun-server-*.jar"))
if len(nailgunMatches) == 0 {
return nil, nil // Safe fallback if nailgun server jar is not installed
}

allJars, _ := filepath.Glob(filepath.Join(libDir, "*.jar"))
if len(allJars) == 0 {
return nil, nil
}
classpath := strings.Join(allJars, ":")

numDaemons := min(runtime.NumCPU(), 2)
basePort := 2113
var cmds []*exec.Cmd
var ports []int

for i := 0; i < numDaemons; i++ {

Check failure on line 147 in internal/librarian/java/daemon.go

View workflow job for this annotation

GitHub Actions / golangci-lint

rangeint: for loop can be modernized using range over int (modernize)

Check failure on line 147 in internal/librarian/java/daemon.go

View workflow job for this annotation

GitHub Actions / golangci-lint

rangeint: for loop can be modernized using range over int (modernize)
port := basePort + i
daemon, err := StartGAPICDaemon(ctx, env, classpath, port)
if err != nil {
for _, c := range cmds {
if c != nil && c.Process != nil {
_ = c.Process.Kill()
}
}
return nil, fmt.Errorf("failed to start daemon on port %d: %w", port, err)
}
cmds = append(cmds, daemon.cmds...)
ports = append(ports, port)
}

activeDaemonMu.Lock()
activeDaemonPorts = ports
activeDaemonMu.Unlock()

os.Setenv("NAILGUN_PORT", fmt.Sprintf("%d", ports[0]))
return &GAPICDaemon{cmds: cmds, Port: ports[0]}, nil
}
38 changes: 38 additions & 0 deletions internal/librarian/java/daemon_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright 2026 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package java

import (
"context"
"testing"
)

func TestGAPICDaemon_StopNil(t *testing.T) {
var daemon *GAPICDaemon
if err := daemon.Stop(); err != nil {
t.Errorf("expected no error when stopping nil daemon, got %v", err)
}
}

func TestStartDaemonIfConfigured_NilConfig(t *testing.T) {
ctx := context.Background()
daemon, err := StartDaemonIfConfigured(ctx, nil)
if err != nil {
t.Fatalf("expected no error for nil config, got %v", err)
}
if daemon != nil {
t.Errorf("expected nil daemon for nil config, got %v", daemon)
}
}
Loading
Loading