feat: write_source_files windows implementation

This commit is contained in:
Derek Cormier 2022-02-17 15:08:24 -08:00 committed by Greg Magolan
parent ba8b737c24
commit 2195e1c69a
3 changed files with 155 additions and 23 deletions

View File

@ -8,30 +8,14 @@ _write_source_files_attrs = {
"is_windows": attr.bool(mandatory = True),
}
def _write_source_files_impl(ctx):
if ctx.attr.is_windows:
fail("write_source_file is not yet implemented for windows")
if (len(ctx.attr.in_files) != len(ctx.attr.out_files)):
fail("in_files and out_files must be the same length")
for i in range(len(ctx.attr.in_files)):
out_file_label = ctx.attr.out_files[i].label
if is_external_label(out_file_label):
fail("out file %s must be a source file in the user workspace" % out_file_label)
if not ctx.files.out_files[i].is_source:
fail("out file %s must be a source file, not a generated file" % out_file_label)
if out_file_label.package != ctx.label.package:
fail("out file %s (in package '%s') must be a source file within the target's package: '%s'" % (out_file_label, out_file_label.package, ctx.label.package))
def _write_source_files_sh(ctx):
updater = ctx.actions.declare_file(
ctx.label.name + "_update.sh",
)
ctx.actions.write(
output = updater,
is_executable = True,
content = """
#!/usr/bin/env bash
set -o errexit -o nounset -o pipefail
@ -54,6 +38,67 @@ chmod 644 "$out"
]),
)
return updater
def _write_source_files_bat(ctx):
updater = ctx.actions.declare_file(
ctx.label.name + "_update.bat",
)
content = """
@rem Generated by write_source_files.bzl, do not edit.
@echo off
set runfiles_dir=%cd%
if defined BUILD_WORKSPACE_DIRECTORY (
cd %BUILD_WORKSPACE_DIRECTORY%
)
""" + "\n".join(["""
set in=%runfiles_dir%\\{in_file}
set out={out_file}
if not defined BUILD_WORKSPACE_DIRECTORY (
@rem Because there's no sandboxing in windows, if we copy over the target
@rem file's symlink it will get copied back into the source directory
@rem during tests. Work around this in tests by deleting the target file
@rem symlink before copying over it.
del %out%
)
echo Copying %in% to %out% in %cd%
copy %in% %out% >NUL
""".format(in_file = ctx.files.in_files[i].short_path.replace("/", "\\"), out_file = ctx.files.out_files[i].short_path).replace("/", "\\")
for i in range(len(ctx.attr.in_files))
])
content = content.replace("\n", "\r\n")
ctx.actions.write(
output = updater,
is_executable = True,
content = content,
)
return updater
def _write_source_files_impl(ctx):
if (len(ctx.attr.in_files) != len(ctx.attr.out_files)):
fail("in_files and out_files must be the same length")
for i in range(len(ctx.attr.in_files)):
out_file_label = ctx.attr.out_files[i].label
if is_external_label(out_file_label):
fail("out file %s must be a source file in the user workspace" % out_file_label)
if not ctx.files.out_files[i].is_source:
fail("out file %s must be a source file, not a generated file" % out_file_label)
if out_file_label.package != ctx.label.package:
fail("out file %s (in package '%s') must be a source file within the target's package: '%s'" % (out_file_label, out_file_label.package, ctx.label.package))
if ctx.attr.is_windows:
updater = _write_source_files_bat(ctx)
else:
updater = _write_source_files_sh(ctx)
return DefaultInfo(
executable = updater,
runfiles = ctx.runfiles(files = ctx.files.in_files),

View File

@ -31,4 +31,4 @@ write_source_files(
"a2.js": ":a-desired",
"b2.js": ":b-desired",
},
)
)

View File

@ -9,7 +9,7 @@ _write_source_files = rule(
executable = True,
)
def _impl(ctx):
def _impl_sh(ctx):
test = ctx.actions.declare_file(
ctx.label.name + "_test.sh",
)
@ -55,9 +55,86 @@ assert_same() {
]),
)
return test
def _impl_bat(ctx):
test = ctx.actions.declare_file(
ctx.label.name + "_test.bat",
)
# BUG: Windows is having trouble executing the symlink to the write_source_files executable,
# but it is able to execute the actual output script so point to that for now.
#
# What we would use if this bug didn't exist:
# write_source_files = ctx.executable.write_source_files_target.short_path.replace("/", "\\")
#
# Instead back out of the runfiles execution directory:
# write_to_source_files_test_test.bat.runfiles/aspect_bazel_lib
# And point to the output script.
write_source_files = "..\\..\\%s" % ctx.executable.write_source_files_target.basename
content = """
@rem Generated by copy_to_directory.bzl, do not edit.
@echo off
@rem Check that in and out files are different
""" + "\n".join(["""
call :assert_different {in_file}, {out_file}
if %errorlevel% neq 0 exit /b 1
""".format(
in_file = ctx.files.in_files[i].short_path.replace("/", "\\"),
out_file = ctx.files.out_files[i].short_path.replace("/", "\\"),
)
for i in range(len(ctx.files.in_files))
]) + """
@rem Write to the source files
call {write_source_files}
if %errorlevel% neq 0 exit /b 1
@rem Check that in and out files are the same
""".format(write_source_files = write_source_files) + "\n".join(["""
call :assert_same {in_file}, {out_file}
if %errorlevel% neq 0 exit /b 1
""".format(
in_file = ctx.files.in_files[i].short_path.replace("/", "\\"),
out_file = ctx.files.out_files[i].short_path.replace("/", "\\"),
)
for i in range(len(ctx.files.in_files))
]) + """
exit /b 0
:assert_different
fc /b %~1 %~2 > nul
if %errorlevel% equ 1 exit /b 0
echo Error: %~1 and %~2 are not different
exit /b 1
:assert_same
fc /b %~1 %~2 > nul
if %errorlevel% equ 0 exit /b 0
echo Error: %~1 and %~2 are not the same
exit /b 1
"""
content = content.replace("\n", "\r\n")
ctx.actions.write(
output = test,
is_executable = True,
content = content
)
return test
def _impl(ctx):
if ctx.attr.is_windows:
test = _impl_bat(ctx)
else:
test = _impl_sh(ctx)
return DefaultInfo(
executable = test,
runfiles = ctx.runfiles(files = [ctx.file.write_source_files_target] + ctx.files.in_files + ctx.files.out_files),
runfiles = ctx.runfiles(
files = [ctx.executable.write_source_files_target] + ctx.files.in_files + ctx.files.out_files,
),
)
_write_source_files_test = rule(
@ -66,7 +143,9 @@ _write_source_files_test = rule(
"write_source_files_target": attr.label(
allow_single_file = True,
executable = True,
cfg = "exec",
# Should be cfg = "exec" but a bazel bug causes a wrong executable symlink on windows
cfg = "target",
mandatory = True,
),
"out_files": attr.label_list(
allow_files = True,
@ -78,6 +157,7 @@ _write_source_files_test = rule(
allow_empty = False,
mandatory = True,
),
"is_windows": attr.bool(mandatory = True),
},
test = True,
)
@ -89,7 +169,10 @@ def write_source_files_test(name, in_files, out_files):
name = name + "_updater",
out_files = out_files,
in_files = in_files,
is_windows = False,
is_windows = select({
"@bazel_tools//src/conditions:host_windows": True,
"//conditions:default": False,
}),
)
# Note that for testing we update the source files in the sandbox,
@ -99,4 +182,8 @@ def write_source_files_test(name, in_files, out_files):
write_source_files_target = name + "_updater",
out_files = out_files,
in_files = in_files,
is_windows = select({
"@bazel_tools//src/conditions:host_windows": True,
"//conditions:default": False,
}),
)