3535
3636LIBRARIAN_DIR = "librarian"
3737GENERATE_REQUEST_FILE = "generate-request.json"
38+ INPUT_DIR = "input"
39+ BUILD_REQUEST_FILE = "build-request.json"
3840SOURCE_DIR = "source"
3941OUTPUT_DIR = "output"
4042REPO_DIR = "repo"
@@ -44,7 +46,7 @@ def _read_json_file(path: str) -> Dict:
4446 """Helper function that reads a json file path and returns the loaded json content.
4547
4648 Args:
47- path (str): The file path to read.
49+ path(str): The file path to read.
4850
4951 Returns:
5052 dict: The parsed JSON content.
@@ -63,11 +65,12 @@ def handle_configure():
6365 logger .info ("'configure' command executed." )
6466
6567
66- def _determine_bazel_rule (api_path : str ) -> str :
68+ def _determine_bazel_rule (api_path : str , source : str ) -> str :
6769 """Executes a `bazelisk query` to find a Bazel rule.
6870
6971 Args:
70- api_path (str): The API path to query for.
72+ api_path(str): The API path to query for.
73+ source(str): The path to the root of the Bazel workspace.
7174
7275 Returns:
7376 str: The discovered Bazel rule.
@@ -81,7 +84,7 @@ def _determine_bazel_rule(api_path: str) -> str:
8184 command = ["bazelisk" , "query" , query ]
8285 result = subprocess .run (
8386 command ,
84- cwd = f" { SOURCE_DIR } /googleapis" ,
87+ cwd = source ,
8588 capture_output = True ,
8689 text = True ,
8790 check = True ,
@@ -114,11 +117,12 @@ def _get_library_id(request_data: Dict) -> str:
114117 return library_id
115118
116119
117- def _build_bazel_target (bazel_rule : str ):
120+ def _build_bazel_target (bazel_rule : str , source : str ):
118121 """Executes `bazelisk build` on a given Bazel rule.
119122
120123 Args:
121- bazel_rule (str): The Bazel rule to build.
124+ bazel_rule(str): The Bazel rule to build.
125+ source(str): The path to the root of the Bazel workspace.
122126
123127 Raises:
124128 ValueError: If the subprocess call fails.
@@ -128,7 +132,7 @@ def _build_bazel_target(bazel_rule: str):
128132 command = ["bazelisk" , "build" , bazel_rule ]
129133 subprocess .run (
130134 command ,
131- cwd = f" { SOURCE_DIR } /googleapis" ,
135+ cwd = source ,
132136 text = True ,
133137 check = True ,
134138 )
@@ -137,12 +141,22 @@ def _build_bazel_target(bazel_rule: str):
137141 raise ValueError (f"Bazel build for { bazel_rule } rule failed." ) from e
138142
139143
140- def _locate_and_extract_artifact (bazel_rule : str , library_id : str ):
144+ def _locate_and_extract_artifact (
145+ bazel_rule : str ,
146+ library_id : str ,
147+ source : str ,
148+ output : str ,
149+ api_path : str ,
150+ ):
141151 """Finds and extracts the tarball artifact from a Bazel build.
142152
143153 Args:
144- bazel_rule (str): The Bazel rule that was built.
145- library_id (str): The ID of the library being generated.
154+ bazel_rule(str): The Bazel rule that was built.
155+ library_id(str): The ID of the library being generated.
156+ source(str): The path to the root of the Bazel workspace.
157+ output(str): The path to the location where generated output
158+ should be stored.
159+ api_path(str): The API path for the artifact
146160
147161 Raises:
148162 ValueError: If failed to locate or extract artifact.
@@ -153,7 +167,7 @@ def _locate_and_extract_artifact(bazel_rule: str, library_id: str):
153167 info_command = ["bazelisk" , "info" , "bazel-bin" ]
154168 result = subprocess .run (
155169 info_command ,
156- cwd = f" { SOURCE_DIR } /googleapis" ,
170+ cwd = source ,
157171 text = True ,
158172 check = True ,
159173 capture_output = True ,
@@ -167,7 +181,8 @@ def _locate_and_extract_artifact(bazel_rule: str, library_id: str):
167181 logger .info (f"Found artifact at: { tarball_path } " )
168182
169183 # 3. Create a staging directory.
170- staging_dir = os .path .join (OUTPUT_DIR , "owl-bot-staging" , library_id )
184+ api_version = api_path .split ("/" )[- 1 ]
185+ staging_dir = os .path .join (output , "owl-bot-staging" , library_id , api_version )
171186 os .makedirs (staging_dir , exist_ok = True )
172187 logger .info (f"Preparing staging directory: { staging_dir } " )
173188
@@ -185,8 +200,7 @@ def _locate_and_extract_artifact(bazel_rule: str, library_id: str):
185200
186201
187202def _run_post_processor ():
188- """Runs the synthtool post-processor on the output directory.
189- """
203+ """Runs the synthtool post-processor on the output directory."""
190204 logger .info ("Running Python post-processor..." )
191205 if SYNTHTOOL_INSTALLED :
192206 command = ["python3" , "-m" , "synthtool.languages.python_mono_repo" ]
@@ -196,29 +210,48 @@ def _run_post_processor():
196210 logger .info ("Python post-processor ran successfully." )
197211
198212
199- def handle_generate ():
213+ def handle_generate (
214+ librarian : str = LIBRARIAN_DIR ,
215+ source : str = SOURCE_DIR ,
216+ output : str = OUTPUT_DIR ,
217+ input : str = INPUT_DIR ,
218+ ):
200219 """The main coordinator for the code generation process.
201220
202221 This function orchestrates the generation of a client library by reading a
203222 `librarian/generate-request.json` file, determining the necessary Bazel rule for each API, and
204223 (in future steps) executing the build.
205224
225+ See https://github.com/googleapis/librarian/blob/main/doc/container-contract.md#generate-container-command
226+
227+ Args:
228+ librarian(str): Path to the directory in the container which contains
229+ the librarian configuration.
230+ source(str): Path to the directory in the container which contains
231+ API protos.
232+ output(str): Path to the directory in the container where code
233+ should be generated.
234+ input(str): The path path to the directory in the container
235+ which contains additional generator input.
236+
206237 Raises:
207238 ValueError: If the `generate-request.json` file is not found or read.
208239 """
209240
210241 try :
211242 # Read a generate-request.json file
212- request_data = _read_json_file (f"{ LIBRARIAN_DIR } /{ GENERATE_REQUEST_FILE } " )
243+ request_data = _read_json_file (f"{ librarian } /{ GENERATE_REQUEST_FILE } " )
213244 library_id = _get_library_id (request_data )
214245
215246 for api in request_data .get ("apis" , []):
216247 api_path = api .get ("path" )
217248 if api_path :
218- bazel_rule = _determine_bazel_rule (api_path )
219- _build_bazel_target (bazel_rule )
220- _locate_and_extract_artifact (bazel_rule , library_id )
221- _run_post_processor ()
249+ bazel_rule = _determine_bazel_rule (api_path , source )
250+ _build_bazel_target (bazel_rule , source )
251+ _locate_and_extract_artifact (
252+ bazel_rule , library_id , source , output , api_path
253+ )
254+ _run_post_processor (output , f"packages/{ library_id } " )
222255
223256 except Exception as e :
224257 raise ValueError ("Generation failed." ) from e
@@ -227,16 +260,17 @@ def handle_generate():
227260 logger .info ("'generate' command executed." )
228261
229262
230- def _run_nox_sessions (sessions : List [str ]):
263+ def _run_nox_sessions (sessions : List [str ], librarian : str ):
231264 """Calls nox for all specified sessions.
232265
233266 Args:
234- path(List[str]): The list of nox sessions to run.
267+ sessions(List[str]): The list of nox sessions to run.
268+ librarian(str): The path to the librarian build configuration directory
235269 """
236- # Read a generate -request.json file
270+ # Read a build -request.json file
237271 current_session = None
238272 try :
239- request_data = _read_json_file (f"{ LIBRARIAN_DIR } /{ GENERATE_REQUEST_FILE } " )
273+ request_data = _read_json_file (f"{ librarian } /{ BUILD_REQUEST_FILE } " )
240274 library_id = _get_library_id (request_data )
241275 for nox_session in sessions :
242276 _run_individual_session (nox_session , library_id )
@@ -263,7 +297,7 @@ def _run_individual_session(nox_session: str, library_id: str):
263297 logger .info (result )
264298
265299
266- def handle_build ():
300+ def handle_build (librarian : str = LIBRARIAN_DIR ):
267301 """The main coordinator for validating client library generation."""
268302 sessions = [
269303 "unit-3.9" ,
@@ -278,7 +312,7 @@ def handle_build():
278312 "mypy" ,
279313 "check_lower_bounds" ,
280314 ]
281- _run_nox_sessions (sessions )
315+ _run_nox_sessions (sessions , librarian )
282316
283317 logger .info ("'build' command executed." )
284318
@@ -303,10 +337,46 @@ def handle_build():
303337 ]:
304338 parser_cmd = subparsers .add_parser (command_name , help = help_text )
305339 parser_cmd .set_defaults (func = handler_map [command_name ])
306-
340+ parser_cmd .add_argument (
341+ "--librarian" ,
342+ type = str ,
343+ help = "Path to the directory in the container which contains the librarian configuration" ,
344+ default = LIBRARIAN_DIR ,
345+ )
346+ parser_cmd .add_argument (
347+ "--input" ,
348+ type = str ,
349+ help = "Path to the directory in the container which contains additional generator input" ,
350+ default = INPUT_DIR ,
351+ )
352+ parser_cmd .add_argument (
353+ "--output" ,
354+ type = str ,
355+ help = "Path to the directory in the container where code should be generated" ,
356+ default = OUTPUT_DIR ,
357+ )
358+ parser_cmd .add_argument (
359+ "--source" ,
360+ type = str ,
361+ help = "Path to the directory in the container which contains API protos" ,
362+ default = SOURCE_DIR ,
363+ )
307364 if len (sys .argv ) == 1 :
308365 parser .print_help (sys .stderr )
309366 sys .exit (1 )
310367
311368 args = parser .parse_args ()
312369 args .func ()
370+
371+ # Pass specific arguments to the handler functions for generate/build
372+ if args .command == "generate" :
373+ args .func (
374+ librarian = args .librarian ,
375+ source = args .source ,
376+ output = args .output ,
377+ input = args .input ,
378+ )
379+ elif args .command == "build" :
380+ args .func (librarian = args .librarian )
381+ else :
382+ args .func ()
0 commit comments