@@ -42,23 +42,25 @@ var (
4242// the entire generation process.
4343func Generate (ctx context.Context , cfg * generate.Config ) error {
4444 slog .Debug ("librariangen: generate command started" )
45- defer cleanupIntermediateFiles (cfg .Context .OutputDir )
45+ outputConfig := & protoc.OutputConfig {
46+ GAPICDir : filepath .Join (cfg .Context .OutputDir , "gapic" ),
47+ GRPCDir : filepath .Join (cfg .Context .OutputDir , "grpc" ),
48+ ProtoDir : filepath .Join (cfg .Context .OutputDir , "proto" ),
49+ }
50+ defer func () {
51+ if err := cleanupIntermediateFiles (outputConfig ); err != nil {
52+ slog .Error ("librariangen: failed to clean up intermediate files" , "error" , err )
53+ }
54+ }()
4655
4756 generateReq := cfg .Request
4857
49- if err := invokeProtoc (ctx , cfg .Context , generateReq ); err != nil {
58+ if err := invokeProtoc (ctx , cfg .Context , generateReq , outputConfig ); err != nil {
5059 return fmt .Errorf ("librariangen: gapic generation failed: %w" , err )
5160 }
52-
53- // Unzip the generated zip file.
54- zipPath := filepath .Join (cfg .Context .OutputDir , "java_gapic.zip" )
55- if err := unzip (zipPath , cfg .Context .OutputDir ); err != nil {
56- return fmt .Errorf ("librariangen: failed to unzip %s: %w" , zipPath , err )
57- }
58-
59- // Unzip the inner temp-codegen.srcjar.
60- srcjarPath := filepath .Join (cfg .Context .OutputDir , "temp-codegen.srcjar" )
61- srcjarDest := filepath .Join (cfg .Context .OutputDir , "java_gapic_srcjar" )
61+ // Unzip the temp-codegen.srcjar.
62+ srcjarPath := filepath .Join (outputConfig .GAPICDir , "temp-codegen.srcjar" )
63+ srcjarDest := outputConfig .GAPICDir
6264 if err := unzip (srcjarPath , srcjarDest ); err != nil {
6365 return fmt .Errorf ("librariangen: failed to unzip %s: %w" , srcjarPath , err )
6466 }
@@ -74,20 +76,28 @@ func Generate(ctx context.Context, cfg *generate.Config) error {
7476// invokeProtoc handles the protoc GAPIC generation logic for the 'generate' CLI command.
7577// It reads a request file, and for each API specified, it invokes protoc
7678// to generate the client library. It returns the module path and the path to the service YAML.
77- func invokeProtoc (ctx context.Context , genCtx * generate.Context , generateReq * message.Library ) error {
79+ func invokeProtoc (ctx context.Context , genCtx * generate.Context , generateReq * message.Library , outputConfig * protoc. OutputConfig ) error {
7880 for _ , api := range generateReq .APIs {
7981 apiServiceDir := filepath .Join (genCtx .SourceDir , api .Path )
8082 slog .Info ("processing api" , "service_dir" , apiServiceDir )
8183 bazelConfig , err := bazelParse (apiServiceDir )
8284 if err != nil {
8385 return fmt .Errorf ("librariangen: failed to parse BUILD.bazel for %s: %w" , apiServiceDir , err )
8486 }
85- args , err := protocBuild (apiServiceDir , bazelConfig , genCtx .SourceDir , genCtx . OutputDir )
87+ args , err := protocBuild (apiServiceDir , bazelConfig , genCtx .SourceDir , outputConfig )
8688 if err != nil {
8789 return fmt .Errorf ("librariangen: failed to build protoc command for api %q in library %q: %w" , api .Path , generateReq .ID , err )
8890 }
91+
92+ // Create protoc output directories.
93+ for _ , dir := range []string {outputConfig .ProtoDir , outputConfig .GRPCDir , outputConfig .GAPICDir } {
94+ if err := os .MkdirAll (dir , 0755 ); err != nil {
95+ return err
96+ }
97+ }
98+
8999 if err := execvRun (ctx , args , genCtx .OutputDir ); err != nil {
90- return fmt .Errorf ("librariangen: protoc failed for api %q in library %q: %w" , api .Path , generateReq .ID , err )
100+ return fmt .Errorf ("librariangen: protoc failed for api %q in library %q: %w, execvRun error: %v " , api .Path , generateReq .ID , err , err )
91101 }
92102 }
93103 return nil
@@ -104,7 +114,7 @@ func moveFiles(sourceDir, targetDir string) error {
104114 newPath := filepath .Join (targetDir , f .Name ())
105115 slog .Debug ("librariangen: moving file" , "from" , oldPath , "to" , newPath )
106116 if err := os .Rename (oldPath , newPath ); err != nil {
107- return fmt .Errorf ("librariangen: failed to move %s to %s: %w" , oldPath , newPath , err )
117+ return fmt .Errorf ("librariangen: failed to move %s to %s: %w, os.Rename error: %v " , oldPath , newPath , err , err )
108118 }
109119 }
110120 return nil
@@ -114,29 +124,43 @@ func restructureOutput(outputDir, libraryID string) error {
114124 slog .Debug ("librariangen: restructuring output directory" , "dir" , outputDir )
115125
116126 // Define source and destination directories.
117- gapicSrcDir := filepath .Join (outputDir , "java_gapic_srcjar" , "src" , "main" , "java" )
118- gapicTestDir := filepath .Join (outputDir , "java_gapic_srcjar" , "src" , "test" , "java" )
119- protoSrcDir := filepath .Join (outputDir , "com" )
120- samplesDir := filepath .Join (outputDir , "java_gapic_srcjar" , "samples" , "snippets" )
127+ gapicSrcDir := filepath .Join (outputDir , "gapic" , "src" , "main" , "java" )
128+ gapicTestDir := filepath .Join (outputDir , "gapic" , "src" , "test" , "java" )
129+ protoSrcDir := filepath .Join (outputDir , "proto" )
130+ resourceNameSrcDir := filepath .Join (outputDir , "gapic" , "proto" , "src" , "main" , "java" )
131+ grpcSrcDir := filepath .Join (outputDir , "grpc" )
132+ samplesDir := filepath .Join (outputDir , "gapic" , "samples" , "snippets" )
121133
134+ // TODO(meltsufin): currently we assume we have a single API variant v1
122135 gapicDestDir := filepath .Join (outputDir , fmt .Sprintf ("google-cloud-%s" , libraryID ), "src" , "main" , "java" )
123136 gapicTestDestDir := filepath .Join (outputDir , fmt .Sprintf ("google-cloud-%s" , libraryID ), "src" , "test" , "java" )
124137 protoDestDir := filepath .Join (outputDir , fmt .Sprintf ("proto-google-cloud-%s-v1" , libraryID ), "src" , "main" , "java" )
138+ resourceNameDestDir := filepath .Join (outputDir , fmt .Sprintf ("proto-google-cloud-%s-v1" , libraryID ), "src" , "main" , "java" )
139+ grpcDestDir := filepath .Join (outputDir , fmt .Sprintf ("grpc-google-cloud-%s-v1" , libraryID ), "src" , "main" , "java" )
125140 samplesDestDir := filepath .Join (outputDir , "samples" , "snippets" )
126141
127142 // Create destination directories.
128- destDirs := []string {gapicDestDir , gapicTestDestDir , protoDestDir , samplesDestDir }
143+ destDirs := []string {gapicDestDir , gapicTestDestDir , protoDestDir , samplesDestDir , grpcDestDir }
129144 for _ , dir := range destDirs {
130145 if err := os .MkdirAll (dir , 0755 ); err != nil {
131146 return err
132147 }
133148 }
134149
135- // Move files.
150+ // The resource name directory is not created if there are no resource names
151+ // to generate. We create it here to avoid errors later.
152+ if _ , err := os .Stat (resourceNameSrcDir ); os .IsNotExist (err ) {
153+ if err := os .MkdirAll (resourceNameSrcDir , 0755 ); err != nil {
154+ return err
155+ }
156+ }
157+
158+ // Move files that won't have conflicts.
136159 moves := map [string ]string {
137160 gapicSrcDir : gapicDestDir ,
138161 gapicTestDir : gapicTestDestDir ,
139162 protoSrcDir : protoDestDir ,
163+ grpcSrcDir : grpcDestDir ,
140164 samplesDir : samplesDestDir ,
141165 }
142166 for src , dest := range moves {
@@ -145,23 +169,51 @@ func restructureOutput(outputDir, libraryID string) error {
145169 }
146170 }
147171
172+ // Merge the resource name files into the proto destination.
173+ if err := copyAndMerge (resourceNameSrcDir , resourceNameDestDir ); err != nil {
174+ return err
175+ }
176+
148177 return nil
149178}
150179
151- func cleanupIntermediateFiles (outputDir string ) {
152- slog .Debug ("librariangen: cleaning up intermediate files" , "dir" , outputDir )
153- filesToRemove := []string {
154- "java_gapic_srcjar" ,
155- "com" ,
156- "java_gapic.zip" ,
157- "temp-codegen.srcjar" ,
180+ // copyAndMerge recursively copies the contents of src to dest, merging directories.
181+ func copyAndMerge (src , dest string ) error {
182+ entries , err := os .ReadDir (src )
183+ if os .IsNotExist (err ) {
184+ return nil
185+ }
186+ if err != nil {
187+ return err
188+ }
189+
190+ for _ , entry := range entries {
191+ srcPath := filepath .Join (src , entry .Name ())
192+ destPath := filepath .Join (dest , entry .Name ())
193+ if entry .IsDir () {
194+ if err := os .MkdirAll (destPath , 0755 ); err != nil {
195+ return err
196+ }
197+ if err := copyAndMerge (srcPath , destPath ); err != nil {
198+ return err
199+ }
200+ } else {
201+ if err := os .Rename (srcPath , destPath ); err != nil {
202+ return fmt .Errorf ("librariangen: failed to move %s to %s: %w, os.Rename error: %v" , srcPath , destPath , err , err )
203+ }
204+ }
158205 }
159- for _ , file := range filesToRemove {
160- path := filepath .Join (outputDir , file )
206+ return nil
207+ }
208+
209+ func cleanupIntermediateFiles (outputConfig * protoc.OutputConfig ) error {
210+ slog .Debug ("librariangen: cleaning up intermediate files" )
211+ for _ , path := range []string {outputConfig .GAPICDir , outputConfig .GRPCDir , outputConfig .ProtoDir } {
161212 if err := os .RemoveAll (path ); err != nil {
162- slog . Error ( "librariangen: failed to clean up intermediate file" , "path" , path , "error" , err )
213+ return fmt . Errorf ( " failed to clean up intermediate file at %s: %w" , path , err )
163214 }
164215 }
216+ return nil
165217}
166218
167219func unzip (src , dest string ) error {
0 commit comments