Skip to content

Commit b2ff68c

Browse files
authored
feat(internal/librarian/java): add RestructureToLibrary helper and unit tests (#6757)
Add RestructureToLibrary helper function to reorganize and relocate generated Java source files (GAPIC, proto, gRPC, and samples) from intermediate directories into target library directories. It also handles conflicting file cleanup and copying of public proto files. For #6516
1 parent 40598bd commit b2ff68c

2 files changed

Lines changed: 236 additions & 0 deletions

File tree

internal/librarian/java/postprocess.go

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -569,3 +569,83 @@ func ToKeepSet(keep []string) map[string]bool {
569569
}
570570
return keepSet
571571
}
572+
573+
// RestructureToLibrary moves all generated source code to the library root directories.
574+
// It also removes conflicting files, and copies public proto files to the library.
575+
func RestructureToLibrary(params postProcessParams, destRoot string, keepSet map[string]bool) error {
576+
tempProtoSrcDir := params.protoDir()
577+
isCommonProtos := params.library.Name == commonProtosLibrary
578+
if !isCommonProtos {
579+
if err := removeConflictingFiles(tempProtoSrcDir); err != nil {
580+
return err
581+
}
582+
}
583+
if err := moveSourcesToLibrary(params, destRoot, keepSet); err != nil {
584+
return err
585+
}
586+
if err := copyProtoFilesToLibrary(params, destRoot); err != nil {
587+
return err
588+
}
589+
return nil
590+
}
591+
592+
// moveSourcesToLibrary relocates the generated Java source files (GAPIC, proto,
593+
// gRPC, resource names, and samples) to their repository destinations.
594+
func moveSourcesToLibrary(params postProcessParams, destRoot string, keepSet map[string]bool) error {
595+
coords := params.coords()
596+
isMonolithic := params.javaAPI.Monolithic
597+
var protoDest, grpcDest, gapicMainDest, gapicTestDest string
598+
// Determine target repository subdirectories based on library structure.
599+
if isMonolithic {
600+
protoDest = filepath.Join(destRoot, "src", "main", "java")
601+
grpcDest = filepath.Join(destRoot, "src", "main", "java")
602+
gapicMainDest = filepath.Join(destRoot, "src", "main")
603+
gapicTestDest = filepath.Join(destRoot, "src", "test")
604+
} else {
605+
protoDest = filepath.Join(destRoot, coords.Proto.ArtifactID, "src", "main", "java")
606+
grpcDest = filepath.Join(destRoot, coords.GRPC.ArtifactID, "src", "main", "java")
607+
gapicMainDest = filepath.Join(destRoot, coords.GAPIC.ArtifactID, "src", "main")
608+
gapicTestDest = filepath.Join(destRoot, coords.GAPIC.ArtifactID, "src", "test")
609+
}
610+
var actions []moveAction
611+
// Collect generated source directories to relocate.
612+
if shouldGenerateProto(params.javaAPI) {
613+
actions = append(actions, moveAction{src: params.protoDir(), dest: protoDest, description: "proto main source files"})
614+
}
615+
if shouldGenerateGRPC(params.javaAPI) {
616+
actions = append(actions, moveAction{src: params.gRPCDir(), dest: grpcDest, description: "gRPC main source files"})
617+
}
618+
if shouldGenerateGAPIC(params.javaAPI) {
619+
actions = append(actions,
620+
moveAction{src: filepath.Join(params.gapicDir(), "src", "main"), dest: gapicMainDest, description: "GAPIC main source files"},
621+
moveAction{src: filepath.Join(params.gapicDir(), "src", "test"), dest: gapicTestDest, description: "GAPIC test source files"},
622+
)
623+
}
624+
if shouldGenerateResourceNames(params.javaAPI) {
625+
actions = append(actions, moveAction{src: filepath.Join(params.gapicDir(), "proto", "src", "main", "java"), dest: protoDest, description: "resource name source files"})
626+
}
627+
if params.includeSamples && shouldGenerateGAPIC(params.javaAPI) {
628+
actions = append(actions, moveAction{src: filepath.Join(params.gapicDir(), "samples", "snippets", "generated", "src", "main", "java"), dest: filepath.Join(destRoot, "samples", "snippets", "generated"), description: "samples"})
629+
}
630+
// Relocate all collected source files to their destinations.
631+
return ApplyMoveActionsToLibrary(actions, destRoot, keepSet)
632+
}
633+
634+
// copyProtoFilesToLibrary copies public proto definition (.proto) files from the
635+
// generator inputs to their target directory structure.
636+
func copyProtoFilesToLibrary(params postProcessParams, destRoot string) error {
637+
if !shouldGenerateProto(params.javaAPI) {
638+
return nil
639+
}
640+
coords := params.coords()
641+
var destProtoDir string
642+
if params.javaAPI.Monolithic {
643+
destProtoDir = filepath.Join(destRoot, "src", "main", "proto")
644+
} else {
645+
destProtoDir = filepath.Join(destRoot, coords.Proto.ArtifactID, "src", "main", "proto")
646+
}
647+
if err := copyProtos(params.protosToCopy, destProtoDir); err != nil {
648+
return fmt.Errorf("failed to copy proto files: %w", err)
649+
}
650+
return nil
651+
}

internal/librarian/java/postprocess_test.go

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1260,6 +1260,162 @@ func TestApplyMoveActionsToLibrary_NonExistentSource(t *testing.T) {
12601260
}
12611261
}
12621262

1263+
func TestRestructureToLibrary(t *testing.T) {
1264+
t.Parallel()
1265+
for _, tc := range []struct {
1266+
name string
1267+
monolithic bool
1268+
includeSamples bool
1269+
filesToWrite map[string]string
1270+
wantFiles map[string]string
1271+
}{
1272+
{
1273+
name: "standard multi-module",
1274+
monolithic: false,
1275+
includeSamples: true,
1276+
filesToWrite: map[string]string{
1277+
"v1/gapic/src/main/java/Foo.java": "class Foo {}",
1278+
"v1/gapic/src/test/FooTest.java": "class FooTest {}",
1279+
"v1/proto/com/google/cloud/test/v1/BarProto.java": "class BarProto {}",
1280+
"v1/grpc/com/google/cloud/test/v1/BarGrpc.java": "class BarGrpc {}",
1281+
"v1/gapic/proto/src/main/java/Resource.java": "class Resource {}",
1282+
"v1/gapic/samples/snippets/generated/src/main/java/Sample.java": "class Sample {}",
1283+
},
1284+
wantFiles: map[string]string{
1285+
"proto-google-cloud-test-v1/src/main/java/com/google/cloud/test/v1/BarProto.java": "class BarProto {}",
1286+
"grpc-google-cloud-test-v1/src/main/java/com/google/cloud/test/v1/BarGrpc.java": "class BarGrpc {}",
1287+
"proto-google-cloud-test-v1/src/main/java/Resource.java": "class Resource {}",
1288+
"google-cloud-test/src/main/java/Foo.java": "class Foo {}",
1289+
"google-cloud-test/src/test/FooTest.java": "class FooTest {}",
1290+
"samples/snippets/generated/Sample.java": "class Sample {}",
1291+
},
1292+
},
1293+
{
1294+
name: "monolithic library",
1295+
monolithic: true,
1296+
includeSamples: false,
1297+
filesToWrite: map[string]string{
1298+
"v1/gapic/src/main/java/Gapic.java": "class Gapic {}",
1299+
"v1/grpc/Grpc.java": "class Grpc {}",
1300+
"v1/proto/Proto.java": "class Proto {}",
1301+
},
1302+
wantFiles: map[string]string{
1303+
"src/main/java/Gapic.java": "class Gapic {}",
1304+
"src/main/java/Grpc.java": "class Grpc {}",
1305+
"src/main/java/Proto.java": "class Proto {}",
1306+
},
1307+
},
1308+
{
1309+
name: "samples disabled",
1310+
monolithic: false,
1311+
includeSamples: false,
1312+
filesToWrite: map[string]string{
1313+
"v1/gapic/src/main/java/Foo.java": "class Foo {}",
1314+
"v1/gapic/samples/snippets/generated/src/main/java/Sample.java": "class Sample {}",
1315+
},
1316+
wantFiles: map[string]string{
1317+
"google-cloud-test/src/main/java/Foo.java": "class Foo {}",
1318+
},
1319+
},
1320+
} {
1321+
t.Run(tc.name, func(t *testing.T) {
1322+
t.Parallel()
1323+
srcDir := t.TempDir()
1324+
destDir := t.TempDir()
1325+
writeFiles(t, srcDir, tc.filesToWrite)
1326+
library := &config.Library{
1327+
Name: "test-lib",
1328+
APIs: []*config.API{{Path: "google/cloud/test/v1", Java: &config.JavaAPI{Monolithic: tc.monolithic}}},
1329+
Java: &config.JavaModule{GroupID: "com.google.cloud", ArtifactID: "google-cloud-test"},
1330+
}
1331+
params := postProcessParams{
1332+
cfg: &config.Config{},
1333+
library: library,
1334+
javaAPI: library.APIs[0].Java,
1335+
outDir: srcDir,
1336+
includeSamples: tc.includeSamples,
1337+
apiBase: "v1",
1338+
}
1339+
if err := RestructureToLibrary(params, destDir, nil); err != nil {
1340+
t.Fatal(err)
1341+
}
1342+
gotFiles := readDirFiles(t, destDir)
1343+
if diff := cmp.Diff(tc.wantFiles, gotFiles); diff != "" {
1344+
t.Errorf("mismatch (-want +got):\n%s", diff)
1345+
}
1346+
})
1347+
}
1348+
}
1349+
1350+
// TestRestructureToLibrary_OverwritesExistingFiles verifies that existing files in the destination are overwritten.
1351+
func TestRestructureToLibrary_OverwritesExistingFiles(t *testing.T) {
1352+
t.Parallel()
1353+
srcDir := t.TempDir()
1354+
destDir := t.TempDir()
1355+
writeFiles(t, srcDir, map[string]string{
1356+
"v1/gapic/src/main/java/Foo.java": "class Foo { /* new content */ }",
1357+
})
1358+
writeFiles(t, destDir, map[string]string{
1359+
"google-cloud-test/src/main/java/Foo.java": "class Foo { /* old content */ }",
1360+
})
1361+
library := &config.Library{
1362+
Name: "test-lib",
1363+
APIs: []*config.API{{Path: "google/cloud/test/v1", Java: &config.JavaAPI{}}},
1364+
Java: &config.JavaModule{GroupID: "com.google.cloud", ArtifactID: "google-cloud-test"},
1365+
}
1366+
params := postProcessParams{
1367+
cfg: &config.Config{},
1368+
library: library,
1369+
javaAPI: library.APIs[0].Java,
1370+
outDir: srcDir,
1371+
includeSamples: false,
1372+
apiBase: "v1",
1373+
}
1374+
// Pass nil keepSet to expect default overwriting of conflicting files.
1375+
if err := RestructureToLibrary(params, destDir, nil); err != nil {
1376+
t.Fatal(err)
1377+
}
1378+
gotFiles := readDirFiles(t, destDir)
1379+
wantFiles := map[string]string{
1380+
"google-cloud-test/src/main/java/Foo.java": "class Foo { /* new content */ }",
1381+
}
1382+
if diff := cmp.Diff(wantFiles, gotFiles); diff != "" {
1383+
t.Errorf("mismatch (-want +got):\n%s", diff)
1384+
}
1385+
}
1386+
1387+
func TestRestructureToLibrary_CommonProtos(t *testing.T) {
1388+
t.Parallel()
1389+
srcDir := t.TempDir()
1390+
destDir := t.TempDir()
1391+
writeFiles(t, srcDir, map[string]string{
1392+
"v1/proto/com/google/cloud/location/LocationsProto.java": "class LocationsProto {}",
1393+
})
1394+
library := &config.Library{
1395+
Name: commonProtosLibrary,
1396+
APIs: []*config.API{{Path: "google/cloud/test/v1", Java: &config.JavaAPI{ProtoArtifactIDOverride: "proto-google-common-protos"}}},
1397+
Java: &config.JavaModule{GroupID: "com.google.cloud", ArtifactID: "google-cloud-" + commonProtosLibrary},
1398+
}
1399+
params := postProcessParams{
1400+
cfg: &config.Config{},
1401+
library: library,
1402+
javaAPI: library.APIs[0].Java,
1403+
outDir: srcDir,
1404+
includeSamples: false,
1405+
apiBase: "v1",
1406+
}
1407+
if err := RestructureToLibrary(params, destDir, nil); err != nil {
1408+
t.Fatal(err)
1409+
}
1410+
gotFiles := readDirFiles(t, destDir)
1411+
wantFiles := map[string]string{
1412+
"proto-google-common-protos/src/main/java/com/google/cloud/location/LocationsProto.java": "class LocationsProto {}",
1413+
}
1414+
if diff := cmp.Diff(wantFiles, gotFiles); diff != "" {
1415+
t.Errorf("mismatch (-want +got):\n%s", diff)
1416+
}
1417+
}
1418+
12631419
func writeFiles(t *testing.T, dir string, files map[string]string) {
12641420
t.Helper()
12651421
if err := os.MkdirAll(dir, 0755); err != nil {

0 commit comments

Comments
 (0)