@@ -78,26 +78,29 @@ func Generate(ctx context.Context, cfg *Config) error {
7878 return fmt .Errorf ("librariangen: invalid configuration: %w" , err )
7979 }
8080 slog .Debug ("librariangen: generate command started" )
81- defer cleanupIntermediateFiles (cfg .OutputDir )
81+ outputConfig := & protoc.OutputConfig {
82+ GAPICDir : filepath .Join (cfg .OutputDir , "gapic" ),
83+ GRPCDir : filepath .Join (cfg .OutputDir , "grpc" ),
84+ ProtoDir : filepath .Join (cfg .OutputDir , "proto" ),
85+ }
86+ defer func () {
87+ if err := cleanupIntermediateFiles (outputConfig ); err != nil {
88+ slog .Error ("librariangen: failed to clean up intermediate files" , "error" , err )
89+ }
90+ }()
8291
8392 generateReq , err := readGenerateReq (cfg .LibrarianDir )
8493 if err != nil {
8594 return fmt .Errorf ("librariangen: failed to read request: %w" , err )
8695 }
8796
88- if err := invokeProtoc (ctx , cfg , generateReq ); err != nil {
97+ if err := invokeProtoc (ctx , cfg , generateReq , outputConfig ); err != nil {
8998 return fmt .Errorf ("librariangen: gapic generation failed: %w" , err )
9099 }
91100
92- // Unzip the generated zip file.
93- zipPath := filepath .Join (cfg .OutputDir , "java_gapic.zip" )
94- if err := unzip (zipPath , cfg .OutputDir ); err != nil {
95- return fmt .Errorf ("librariangen: failed to unzip %s: %w" , zipPath , err )
96- }
97-
98- // Unzip the inner temp-codegen.srcjar.
99- srcjarPath := filepath .Join (cfg .OutputDir , "temp-codegen.srcjar" )
100- srcjarDest := filepath .Join (cfg .OutputDir , "java_gapic_srcjar" )
101+ // Unzip the temp-codegen.srcjar.
102+ srcjarPath := filepath .Join (outputConfig .GAPICDir , "temp-codegen.srcjar" )
103+ srcjarDest := outputConfig .GAPICDir
101104 if err := unzip (srcjarPath , srcjarDest ); err != nil {
102105 return fmt .Errorf ("librariangen: failed to unzip %s: %w" , srcjarPath , err )
103106 }
@@ -113,20 +116,28 @@ func Generate(ctx context.Context, cfg *Config) error {
113116// invokeProtoc handles the protoc GAPIC generation logic for the 'generate' CLI command.
114117// It reads a request file, and for each API specified, it invokes protoc
115118// to generate the client library. It returns the module path and the path to the service YAML.
116- func invokeProtoc (ctx context.Context , cfg * Config , generateReq * message.Library ) error {
119+ func invokeProtoc (ctx context.Context , cfg * Config , generateReq * message.Library , outputConfig * protoc. OutputConfig ) error {
117120 for _ , api := range generateReq .APIs {
118121 apiServiceDir := filepath .Join (cfg .SourceDir , api .Path )
119122 slog .Info ("processing api" , "service_dir" , apiServiceDir )
120123 bazelConfig , err := bazelParse (apiServiceDir )
121124 if err != nil {
122125 return fmt .Errorf ("librariangen: failed to parse BUILD.bazel for %s: %w" , apiServiceDir , err )
123126 }
124- args , err := protocBuild (apiServiceDir , bazelConfig , cfg .SourceDir , cfg . OutputDir )
127+ args , err := protocBuild (apiServiceDir , bazelConfig , cfg .SourceDir , outputConfig )
125128 if err != nil {
126129 return fmt .Errorf ("librariangen: failed to build protoc command for api %q in library %q: %w" , api .Path , generateReq .ID , err )
127130 }
131+
132+ // Create protoc output directories.
133+ for _ , dir := range []string {outputConfig .ProtoDir , outputConfig .GRPCDir , outputConfig .GAPICDir } {
134+ if err := os .MkdirAll (dir , 0755 ); err != nil {
135+ return err
136+ }
137+ }
138+
128139 if err := execvRun (ctx , args , cfg .OutputDir ); err != nil {
129- return fmt .Errorf ("librariangen: protoc failed for api %q in library %q: %w" , api .Path , generateReq .ID , err )
140+ return fmt .Errorf ("librariangen: protoc failed for api %q in library %q: %w, execvRun error: %v " , api .Path , generateReq .ID , err , err )
130141 }
131142 }
132143 return nil
@@ -158,7 +169,7 @@ func moveFiles(sourceDir, targetDir string) error {
158169 newPath := filepath .Join (targetDir , f .Name ())
159170 slog .Debug ("librariangen: moving file" , "from" , oldPath , "to" , newPath )
160171 if err := os .Rename (oldPath , newPath ); err != nil {
161- return fmt .Errorf ("librariangen: failed to move %s to %s: %w" , oldPath , newPath , err )
172+ return fmt .Errorf ("librariangen: failed to move %s to %s: %w, os.Rename error: %v " , oldPath , newPath , err , err )
162173 }
163174 }
164175 return nil
@@ -168,29 +179,43 @@ func restructureOutput(outputDir, libraryID string) error {
168179 slog .Debug ("librariangen: restructuring output directory" , "dir" , outputDir )
169180
170181 // Define source and destination directories.
171- gapicSrcDir := filepath .Join (outputDir , "java_gapic_srcjar" , "src" , "main" , "java" )
172- gapicTestDir := filepath .Join (outputDir , "java_gapic_srcjar" , "src" , "test" , "java" )
173- protoSrcDir := filepath .Join (outputDir , "com" )
174- samplesDir := filepath .Join (outputDir , "java_gapic_srcjar" , "samples" , "snippets" )
175-
182+ gapicSrcDir := filepath .Join (outputDir , "gapic" , "src" , "main" , "java" )
183+ gapicTestDir := filepath .Join (outputDir , "gapic" , "src" , "test" , "java" )
184+ protoSrcDir := filepath .Join (outputDir , "proto" )
185+ resourceNameSrcDir := filepath .Join (outputDir , "gapic" , "proto" , "src" , "main" , "java" )
186+ grpcSrcDir := filepath .Join (outputDir , "grpc" )
187+ samplesDir := filepath .Join (outputDir , "gapic" , "samples" , "snippets" )
188+
189+ // TODO(meltsufin): currently we assume we have a single API variant v1
176190 gapicDestDir := filepath .Join (outputDir , fmt .Sprintf ("google-cloud-%s" , libraryID ), "src" , "main" , "java" )
177191 gapicTestDestDir := filepath .Join (outputDir , fmt .Sprintf ("google-cloud-%s" , libraryID ), "src" , "test" , "java" )
178192 protoDestDir := filepath .Join (outputDir , fmt .Sprintf ("proto-google-cloud-%s-v1" , libraryID ), "src" , "main" , "java" )
193+ resourceNameDestDir := filepath .Join (outputDir , fmt .Sprintf ("proto-google-cloud-%s-v1" , libraryID ), "src" , "main" , "java" )
194+ grpcDestDir := filepath .Join (outputDir , fmt .Sprintf ("grpc-google-cloud-%s-v1" , libraryID ), "src" , "main" , "java" )
179195 samplesDestDir := filepath .Join (outputDir , "samples" , "snippets" )
180196
181197 // Create destination directories.
182- destDirs := []string {gapicDestDir , gapicTestDestDir , protoDestDir , samplesDestDir }
198+ destDirs := []string {gapicDestDir , gapicTestDestDir , protoDestDir , samplesDestDir , grpcDestDir }
183199 for _ , dir := range destDirs {
184200 if err := os .MkdirAll (dir , 0755 ); err != nil {
185201 return err
186202 }
187203 }
188204
189- // Move files.
205+ // The resource name directory is not created if there are no resource names
206+ // to generate. We create it here to avoid errors later.
207+ if _ , err := os .Stat (resourceNameSrcDir ); os .IsNotExist (err ) {
208+ if err := os .MkdirAll (resourceNameSrcDir , 0755 ); err != nil {
209+ return err
210+ }
211+ }
212+
213+ // Move files that won't have conflicts.
190214 moves := map [string ]string {
191215 gapicSrcDir : gapicDestDir ,
192216 gapicTestDir : gapicTestDestDir ,
193217 protoSrcDir : protoDestDir ,
218+ grpcSrcDir : grpcDestDir ,
194219 samplesDir : samplesDestDir ,
195220 }
196221 for src , dest := range moves {
@@ -199,23 +224,51 @@ func restructureOutput(outputDir, libraryID string) error {
199224 }
200225 }
201226
227+ // Merge the resource name files into the proto destination.
228+ if err := copyAndMerge (resourceNameSrcDir , resourceNameDestDir ); err != nil {
229+ return err
230+ }
231+
202232 return nil
203233}
204234
205- func cleanupIntermediateFiles (outputDir string ) {
206- slog .Debug ("librariangen: cleaning up intermediate files" , "dir" , outputDir )
207- filesToRemove := []string {
208- "java_gapic_srcjar" ,
209- "com" ,
210- "java_gapic.zip" ,
211- "temp-codegen.srcjar" ,
235+ // copyAndMerge recursively copies the contents of src to dest, merging directories.
236+ func copyAndMerge (src , dest string ) error {
237+ entries , err := os .ReadDir (src )
238+ if os .IsNotExist (err ) {
239+ return nil
240+ }
241+ if err != nil {
242+ return err
243+ }
244+
245+ for _ , entry := range entries {
246+ srcPath := filepath .Join (src , entry .Name ())
247+ destPath := filepath .Join (dest , entry .Name ())
248+ if entry .IsDir () {
249+ if err := os .MkdirAll (destPath , 0755 ); err != nil {
250+ return err
251+ }
252+ if err := copyAndMerge (srcPath , destPath ); err != nil {
253+ return err
254+ }
255+ } else {
256+ if err := os .Rename (srcPath , destPath ); err != nil {
257+ return fmt .Errorf ("librariangen: failed to move %s to %s: %w, os.Rename error: %v" , srcPath , destPath , err , err )
258+ }
259+ }
212260 }
213- for _ , file := range filesToRemove {
214- path := filepath .Join (outputDir , file )
261+ return nil
262+ }
263+
264+ func cleanupIntermediateFiles (outputConfig * protoc.OutputConfig ) error {
265+ slog .Debug ("librariangen: cleaning up intermediate files" )
266+ for _ , path := range []string {outputConfig .GAPICDir , outputConfig .GRPCDir , outputConfig .ProtoDir } {
215267 if err := os .RemoveAll (path ); err != nil {
216- slog . Error ( "librariangen: failed to clean up intermediate file" , "path" , path , "error" , err )
268+ return fmt . Errorf ( " failed to clean up intermediate file at %s: %w" , path , err )
217269 }
218270 }
271+ return nil
219272}
220273
221274func unzip (src , dest string ) error {
0 commit comments