@@ -8,7 +8,7 @@ load(":utils.bzl", "utils")
88WriteSourceFileInfo = provider (
99 "Provider for write_source_file targets" ,
1010 fields = {
11- "executable " : "Executable that updates the source files " ,
11+ "args " : "Arguments to pass " ,
1212 },
1313)
1414
@@ -213,177 +213,29 @@ _write_source_file_attrs = {
213213 "verbosity" : attr .string (
214214 values = ["full" , "short" , "quiet" ],
215215 ),
216- "_windows_constraint" : attr .label (default = "@platforms//os:windows" ),
217- "_macos_constraint" : attr .label (default = "@platforms//os:macos" ),
216+ "_writer_bin" : attr .label (
217+ default = "//tools/write_source_files" ,
218+ executable = True ,
219+ # Intentionally use the target platform since the target is always meant to be `bazel run`
220+ # on the host machine but we don't want to transition it to the host platform and have the
221+ # generated file rebuilt in a separate output tree. Target platform should always be equal
222+ # to the host platform when using `write_source_files`.
223+ cfg = "target" ,
224+ ),
218225}
219226
220- def _write_source_file_sh (ctx , paths ):
221- is_macos = ctx .target_platform_has_constraint (ctx .attr ._macos_constraint [platform_common .ConstraintValueInfo ])
222-
223- updater = ctx .actions .declare_file (
224- ctx .label .name + "_update.sh" ,
225- )
226-
227- additional_update_scripts = []
228- for target in ctx .attr .additional_update_targets :
229- additional_update_scripts .append (target [WriteSourceFileInfo ].executable )
230-
231- contents = ["""#!/usr/bin/env bash
232- set -o errexit -o nounset -o pipefail
233- runfiles_dir=$PWD
234- # BUILD_WORKSPACE_DIRECTORY not set when running as a test, uses the sandbox instead
235- if [[ ! -z "${BUILD_WORKSPACE_DIRECTORY:-}" ]]; then
236- cd "$BUILD_WORKSPACE_DIRECTORY"
237- fi""" ]
238-
239- if ctx .attr .executable :
240- executable_file = "chmod +x \" $out\" "
241- executable_dir = "chmod -R +x \" $out\" "
242- else :
243- executable_file = "chmod -x \" $out\" "
244- if is_macos :
245- # -x+X doesn't work on macos so we have to find files and remove the execute bits only from those
246- executable_dir = "find \" $out\" -type f | xargs chmod -x"
247- else :
248- # Remove execute/search bit recursively from files bit not directories: https://superuser.com/a/434418
249- executable_dir = "chmod -R -x+X \" $out\" "
250-
251- progress_message_dir = ""
252- progress_message_file = ""
253- if ctx .attr .verbosity == "full" :
254- progress_message_dir = "echo \" Copying directory $in to $out in $PWD\" "
255- progress_message_file = "echo \" Copying file $in to $out in $PWD\" "
256- elif ctx .attr .verbosity == "short" :
257- progress_message_dir = "echo \" Updating directory $out\" "
258- progress_message_file = "echo \" Updating file $out\" "
259-
260- for in_path , out_path in paths :
261- contents .append ("""
262- in=$runfiles_dir/{in_path}
263- out={out_path}
264-
265- mkdir -p "$(dirname "$out")"
266- if [[ -f "$in" ]]; then
267- {progress_message_file}
268- # in case `cp` from previous command was terminated midway which can result in read-only files/dirs
269- chmod -R +w "$out" > /dev/null 2>&1 || true
270- rm -Rf "$out"
271- cp -f "$in" "$out"
272- # cp should make the file writable but call chmod anyway as a defense in depth
273- chmod +w "$out"
274- # cp should make the file not-executable but set the desired execute bit in both cases as a defense in depth
275- {executable_file}
276- else
277- {progress_message_dir}
278- # in case `cp` from previous command was terminated midway which can result in read-only files/dirs
279- chmod -R +w "$out" > /dev/null 2>&1 || true
280- rm -Rf "$out"/{{*,.[!.]*}}
281- mkdir -p "$out"
282- cp -fRL "$in"/. "$out"
283- chmod -R +w "$out"
284- {executable_dir}
285- fi
286- """ .format (
287- in_path = in_path ,
288- out_path = out_path ,
289- executable_file = executable_file ,
290- executable_dir = executable_dir ,
291- progress_message_dir = progress_message_dir ,
292- progress_message_file = progress_message_file ,
293- ))
294-
295- contents .extend ([
296- "cd \" $runfiles_dir\" " ,
297- "# Run the update scripts for all write_source_file deps" ,
298- ])
299- for update_script in additional_update_scripts :
300- contents .append ("./\" {update_script}\" " .format (update_script = update_script .short_path ))
301-
302- ctx .actions .write (
303- output = updater ,
304- is_executable = True ,
305- content = "\n " .join (contents ),
306- )
307-
308- return updater
309-
310- def _write_source_file_bat (ctx , paths ):
311- updater = ctx .actions .declare_file (
312- ctx .label .name + "_update.bat" ,
313- )
314-
315- additional_update_scripts = []
316- for target in ctx .attr .additional_update_targets :
317- if target [DefaultInfo ].files_to_run and target [DefaultInfo ].files_to_run .executable :
318- additional_update_scripts .append (target [DefaultInfo ].files_to_run .executable )
319- else :
320- fail ("additional_update_targets target %s does not provide an executable" )
321-
322- contents = ["""@rem @generated by @aspect_bazel_lib//:lib/private:write_source_file.bzl
323- @echo off
324- set runfiles_dir=%cd%
325- if defined BUILD_WORKSPACE_DIRECTORY (
326- cd %BUILD_WORKSPACE_DIRECTORY%
327- )""" ]
328-
329- progress_message = ""
330- if ctx .attr .verbosity == "full" :
331- progress_message = "echo Copying %in% to %out% in %cd%"
332- elif ctx .attr .verbosity == "short" :
333- progress_message = "echo Updating %out%"
334-
335- for in_path , out_path in paths :
336- contents .append ("""
337- set in=%runfiles_dir%\\ {in_path}
338- set out={out_path}
339-
340- if not defined BUILD_WORKSPACE_DIRECTORY (
341- @rem Because there's no sandboxing in windows, if we copy over the target
342- @rem file's symlink it will get copied back into the source directory
343- @rem during tests. Work around this in tests by deleting the target file
344- @rem symlink before copying over it.
345- del %out%
346- )
347-
348- {progress_message}
349-
350- if exist "%in%\\ *" (
351- mkdir "%out%" >NUL 2>NUL
352- robocopy "%in%" "%out%" /E >NUL
353- ) else (
354- copy %in% %out% >NUL
355- )
356- """ .format (
357- in_path = in_path .replace ("/" , "\\ " ),
358- out_path = out_path .replace ("/" , "\\ " ),
359- progress_message = progress_message ,
360- ))
361-
362- contents .extend ([
363- "cd %runfiles_dir%" ,
364- "@rem Run the update scripts for all write_source_file deps" ,
365- ])
366- for update_script in additional_update_scripts :
367- contents .append ("call {update_script}" .format (update_script = update_script .short_path ))
368-
369- ctx .actions .write (
370- output = updater ,
371- is_executable = True ,
372- content = "\n " .join (contents ).replace ("\n " , "\r \n " ),
373- )
374- return updater
227+ def _map_arg (item ):
228+ return [item [0 ], item [1 ], str (item [2 ])]
375229
376230def _write_source_file_impl (ctx ):
377- is_windows = ctx .target_platform_has_constraint (ctx .attr ._windows_constraint [platform_common .ConstraintValueInfo ])
378-
379231 out_file = Label (ctx .attr .out_file ) if ctx .attr .out_file else None
380232
381233 if out_file and not ctx .attr .in_file :
382234 fail ("in_file must be specified if out_file is set" )
383235 if ctx .attr .in_file and not out_file :
384236 fail ("out_file must be specified if in_file is set" )
385237
386- paths = []
238+ direct_args = []
387239 runfiles = []
388240
389241 if ctx .attr .in_file and out_file :
@@ -403,12 +255,26 @@ def _write_source_file_impl(ctx):
403255 fail (msg )
404256
405257 out_path = "/" .join ([out_file .package , out_file .name ]) if out_file .package else out_file .name
406- paths .append ((in_path , out_path ))
258+ direct_args .append ((in_path , out_path , ctx . attr . executable ))
407259
408- if is_windows :
409- updater = _write_source_file_bat (ctx , paths )
410- else :
411- updater = _write_source_file_sh (ctx , paths )
260+ arg_depset = depset (
261+ direct_args ,
262+ transitive = [dep [WriteSourceFileInfo ].args for dep in ctx .attr .additional_update_targets ],
263+ )
264+
265+ args = ctx .actions .args ()
266+ args .add_all (arg_depset , map_each = _map_arg )
267+
268+ updater = ctx .actions .declare_file (ctx .attr .name + "_updater" )
269+ ctx .actions .symlink (
270+ output = updater ,
271+ target_file = ctx .executable ._writer_bin ,
272+ is_executable = True ,
273+ )
274+
275+ args_file = ctx .actions .declare_file (ctx .attr .name + "_args" )
276+ ctx .actions .write (args_file , args )
277+ runfiles .append (args_file )
412278
413279 runfiles = ctx .runfiles (
414280 files = runfiles ,
@@ -424,7 +290,12 @@ def _write_source_file_impl(ctx):
424290 runfiles = runfiles ,
425291 ),
426292 WriteSourceFileInfo (
427- executable = updater ,
293+ args = arg_depset ,
294+ ),
295+ RunEnvironmentInfo (
296+ environment = {
297+ "ARGS_PATH" : args_file .short_path ,
298+ },
428299 ),
429300 ]
430301
0 commit comments