Skip to content

Commit 50b95f2

Browse files
authored
feat(internal/container/java): generate pom.xml files (#2682)
Add creation of `pom.xml` files for the generated Java client libraries. We assume that the `google-cloud-pom-parent` and `google-cloud-jar-parent` are published from a separate repository. For now, the version is hard-coded in the templates. The intent here is that we will no longer have to distinguish between libraries in the monorepo vs split repos in terms of parent poms. `run-genrate-library.sh` is also updated to not only generate the sample library, but to build it as well. Fixes #2657
1 parent e87ae8a commit 50b95f2

19 files changed

+1283
-4
lines changed

all_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ var ignoredExts = map[string]bool{
5252
".txt": true,
5353
".webp": true,
5454
".sh": true,
55+
".xml": true,
56+
".tmpl": true,
5557
}
5658

5759
var ignoredDirs = []string{

internal/container/java/generate/generator.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import (
2828
"github.com/googleapis/librarian/internal/container/java/execv"
2929
"github.com/googleapis/librarian/internal/container/java/languagecontainer/generate"
3030
"github.com/googleapis/librarian/internal/container/java/message"
31+
"github.com/googleapis/librarian/internal/container/java/pom"
3132
"github.com/googleapis/librarian/internal/container/java/protoc"
3233
)
3334

@@ -69,6 +70,11 @@ func Generate(ctx context.Context, cfg *generate.Config) error {
6970
return fmt.Errorf("librariangen: failed to restructure output: %w", err)
7071
}
7172

73+
// Generate pom.xml files
74+
if err := pom.Generate(cfg.Context.OutputDir, generateReq.ID); err != nil {
75+
return fmt.Errorf("librariangen: failed to generate poms for API %s: %w", generateReq.ID, err)
76+
}
77+
7278
slog.Debug("librariangen: generate command finished")
7379
return nil
7480
}

internal/container/java/pom/pom.go

Lines changed: 261 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,261 @@
1+
// Copyright 2025 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package pom
16+
17+
import (
18+
"embed"
19+
"fmt"
20+
"os"
21+
"path/filepath"
22+
"sort"
23+
"strings"
24+
"text/template"
25+
)
26+
27+
//go:embed template/*.tmpl
28+
var templatesFS embed.FS
29+
30+
var templates *template.Template
31+
32+
func init() {
33+
templates = template.Must(template.New("").ParseFS(templatesFS, "template/*.tmpl"))
34+
}
35+
36+
// Module represents a Maven module.
37+
type Module struct {
38+
GroupId string
39+
ArtifactId string
40+
Version string
41+
}
42+
43+
// Generate generates the pom.xml files for a library.
44+
// Precondition: module directories exist except for for the *-bom.
45+
func Generate(libraryPath, libraryID string) error {
46+
// 1. Create main module from libraryID.
47+
mainModule := &Module{
48+
GroupId: "com.google.cloud",
49+
ArtifactId: fmt.Sprintf("google-cloud-%s", libraryID),
50+
Version: "0.0.1-SNAPSHOT", // Default version
51+
}
52+
53+
// 2. Find other modules (proto, grpc).
54+
modules, protoModules, grpcModules, err := findModules(libraryPath, mainModule)
55+
if err != nil {
56+
return fmt.Errorf("could not find modules: %w", err)
57+
}
58+
59+
// 3. Render templates
60+
if err := renderTemplates(libraryPath, mainModule, modules, protoModules, grpcModules, libraryID); err != nil {
61+
return fmt.Errorf("could not render templates: %w", err)
62+
}
63+
64+
return nil
65+
}
66+
67+
func findModules(libraryPath string, mainModule *Module) (map[string]*Module, []*Module, []*Module, error) {
68+
modules := make(map[string]*Module)
69+
protoModules := []*Module{}
70+
grpcModules := []*Module{}
71+
72+
modules[mainModule.ArtifactId] = mainModule
73+
74+
files, err := os.ReadDir(libraryPath)
75+
if err != nil {
76+
return nil, nil, nil, err
77+
}
78+
79+
for _, f := range files {
80+
if f.IsDir() {
81+
if strings.HasPrefix(f.Name(), "proto-") {
82+
module := &Module{
83+
GroupId: "com.google.api.grpc",
84+
ArtifactId: f.Name(),
85+
Version: mainModule.Version,
86+
}
87+
modules[f.Name()] = module
88+
protoModules = append(protoModules, module)
89+
} else if strings.HasPrefix(f.Name(), "grpc-") {
90+
module := &Module{
91+
GroupId: "com.google.api.grpc",
92+
ArtifactId: f.Name(),
93+
Version: mainModule.Version,
94+
}
95+
modules[f.Name()] = module
96+
grpcModules = append(grpcModules, module)
97+
}
98+
}
99+
}
100+
return modules, protoModules, grpcModules, nil
101+
}
102+
103+
func renderTemplates(libraryPath string, mainModule *Module, modules map[string]*Module, protoModules, grpcModules []*Module, libraryID string) error {
104+
// Render the parent pom.xml
105+
if err := renderParentPom(libraryPath, mainModule, modules, libraryID); err != nil {
106+
return err
107+
}
108+
109+
for path, module := range modules {
110+
if strings.HasPrefix(path, "proto-") {
111+
if err := renderProtoPom(filepath.Join(libraryPath, path), mainModule, module); err != nil {
112+
return err
113+
}
114+
}
115+
if strings.HasPrefix(path, "grpc-") {
116+
protoArtifactId := strings.Replace(module.ArtifactId, "grpc-", "proto-", 1)
117+
protoModule, ok := modules[protoArtifactId]
118+
if !ok {
119+
return fmt.Errorf("grpc module %s exists without a corresponding proto module", module.ArtifactId)
120+
}
121+
if err := renderGrpcPom(filepath.Join(libraryPath, path), mainModule, module, protoModule); err != nil {
122+
return err
123+
}
124+
}
125+
}
126+
mainArtifactDir := filepath.Join(libraryPath, mainModule.ArtifactId)
127+
if err := renderCloudPom(mainArtifactDir, mainModule, protoModules, grpcModules, libraryID); err != nil {
128+
return err
129+
}
130+
bomDir := filepath.Join(libraryPath, mainModule.ArtifactId+"-bom")
131+
if err := renderBomPom(bomDir, mainModule, modules, libraryID); err != nil {
132+
return err
133+
}
134+
return nil
135+
}
136+
137+
func renderParentPom(libraryPath string, mainModule *Module, modules map[string]*Module, libraryID string) error {
138+
var moduleList []*Module
139+
for _, m := range modules {
140+
moduleList = append(moduleList, m)
141+
}
142+
sort.Slice(moduleList, func(i, j int) bool {
143+
return moduleList[i].ArtifactId < moduleList[j].ArtifactId
144+
})
145+
146+
data := struct {
147+
MainModule *Module
148+
Name string
149+
Modules []*Module
150+
}{
151+
MainModule: mainModule,
152+
Name: fmt.Sprintf("Google Cloud %s", libraryID),
153+
Modules: moduleList,
154+
}
155+
return renderPom(filepath.Join(libraryPath, "pom.xml"), "parent_pom.xml.tmpl", data)
156+
}
157+
158+
// renderPom executes a template with the given data and writes the output to the outputPath.
159+
func renderPom(outputPath, templateName string, data interface{}) error {
160+
pomFile, err := os.Create(outputPath)
161+
if err != nil {
162+
return err
163+
}
164+
defer pomFile.Close()
165+
166+
return templates.ExecuteTemplate(pomFile, templateName, data)
167+
}
168+
169+
func renderProtoPom(modulePath string, mainModule, module *Module) error {
170+
parentModule := &Module{
171+
GroupId: mainModule.GroupId,
172+
ArtifactId: mainModule.ArtifactId + "-parent",
173+
Version: mainModule.Version,
174+
}
175+
176+
data := struct {
177+
MainModule *Module
178+
Module *Module
179+
ParentModule *Module
180+
}{
181+
MainModule: mainModule,
182+
Module: module,
183+
ParentModule: parentModule,
184+
}
185+
return renderPom(filepath.Join(modulePath, "pom.xml"), "proto_pom.xml.tmpl", data)
186+
}
187+
188+
func renderGrpcPom(modulePath string, mainModule, module, protoModule *Module) error {
189+
parentModule := &Module{
190+
GroupId: mainModule.GroupId,
191+
ArtifactId: mainModule.ArtifactId + "-parent",
192+
Version: mainModule.Version,
193+
}
194+
195+
data := struct {
196+
MainModule *Module
197+
Module *Module
198+
ParentModule *Module
199+
ProtoModule *Module
200+
}{
201+
MainModule: mainModule,
202+
Module: module,
203+
ParentModule: parentModule,
204+
ProtoModule: protoModule,
205+
}
206+
return renderPom(filepath.Join(modulePath, "pom.xml"), "grpc_pom.xml.tmpl", data)
207+
}
208+
209+
func renderCloudPom(modulePath string, mainModule *Module, protoModules, grpcModules []*Module, libraryID string) error {
210+
parentModule := &Module{
211+
GroupId: mainModule.GroupId,
212+
ArtifactId: mainModule.ArtifactId + "-parent",
213+
Version: mainModule.Version,
214+
}
215+
216+
data := struct {
217+
Module *Module
218+
Name string
219+
Description string
220+
ParentModule *Module
221+
ProtoModules []*Module
222+
GrpcModules []*Module
223+
Repo string
224+
}{
225+
Module: mainModule,
226+
Name: fmt.Sprintf("Google Cloud %s", libraryID),
227+
Description: fmt.Sprintf("Google Cloud %s client", libraryID),
228+
ParentModule: parentModule,
229+
ProtoModules: protoModules,
230+
GrpcModules: grpcModules,
231+
Repo: "googleapis/google-cloud-java",
232+
}
233+
234+
return renderPom(filepath.Join(modulePath, "pom.xml"), "cloud_pom.xml.tmpl", data)
235+
}
236+
237+
func renderBomPom(modulePath string, mainModule *Module, modules map[string]*Module, libraryID string) error {
238+
if _, err := os.Stat(modulePath); os.IsNotExist(err) {
239+
if err := os.MkdirAll(modulePath, 0755); err != nil {
240+
return err
241+
}
242+
}
243+
var moduleList []*Module
244+
for _, m := range modules {
245+
moduleList = append(moduleList, m)
246+
}
247+
sort.Slice(moduleList, func(i, j int) bool {
248+
return moduleList[i].ArtifactId < moduleList[j].ArtifactId
249+
})
250+
251+
data := struct {
252+
MainModule *Module
253+
Name string
254+
Modules []*Module
255+
}{
256+
MainModule: mainModule,
257+
Name: fmt.Sprintf("Google Cloud %s", libraryID),
258+
Modules: moduleList,
259+
}
260+
return renderPom(filepath.Join(modulePath, "pom.xml"), "bom_pom.xml.tmpl", data)
261+
}

0 commit comments

Comments
 (0)