@@ -78,26 +78,29 @@ func Generate(ctx context.Context, cfg *Config) error {
7878return fmt .Errorf ("librariangen: invalid configuration: %w" , err )
7979}
8080slog .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
8392generateReq , err := readGenerateReq (cfg .LibrarianDir )
8493if err != nil {
8594return 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 {
8998return 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
101104if err := unzip (srcjarPath , srcjarDest ); err != nil {
102105return 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 {
117120for _ , api := range generateReq .APIs {
118121apiServiceDir := filepath .Join (cfg .SourceDir , api .Path )
119122slog .Info ("processing api" , "service_dir" , apiServiceDir )
120123bazelConfig , err := bazelParse (apiServiceDir )
121124if err != nil {
122125return 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 )
125128if err != nil {
126129return 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+
128139if 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}
132143return nil
@@ -158,7 +169,7 @@ func moveFiles(sourceDir, targetDir string) error {
158169newPath := filepath .Join (targetDir , f .Name ())
159170slog .Debug ("librariangen: moving file" , "from" , oldPath , "to" , newPath )
160171if 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}
164175return nil
@@ -168,29 +179,43 @@ func restructureOutput(outputDir, libraryID string) error {
168179slog .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
176190gapicDestDir := filepath .Join (outputDir , fmt .Sprintf ("google-cloud-%s" , libraryID ), "src" , "main" , "java" )
177191gapicTestDestDir := filepath .Join (outputDir , fmt .Sprintf ("google-cloud-%s" , libraryID ), "src" , "test" , "java" )
178192protoDestDir := 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" )
179195samplesDestDir := 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 }
183199for _ , dir := range destDirs {
184200if err := os .MkdirAll (dir , 0755 ); err != nil {
185201return 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.
190214moves := map [string ]string {
191215gapicSrcDir : gapicDestDir ,
192216gapicTestDir : gapicTestDestDir ,
193217protoSrcDir : protoDestDir ,
218+ grpcSrcDir : grpcDestDir ,
194219samplesDir : samplesDestDir ,
195220}
196221for 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+
202232return 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 } {
215267if 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