Skip to content

Commit 2afd091

Browse files
authored
micro_benchmarks.py: Run the project from an isolated directory (#2330)
* micro_benchmarks.py: Run the project from an isolated directory Problem: When running `MicroBenchmarks.csproj` for wasm, the build does: 1. `dotnet restore /Users/radical/dev/performance/src/benchmarks/micro/MicroBenchmarks.csproj ...` 2. `dotnet build /Users/radical/dev/performance/src/benchmarks/micro/MicroBenchmarks.csproj ...` - which emits to `./artifacts/{obj, bin}/MicroBenchmarks/` 3. `dotnet run --project /Users/radical/dev/performance/src/benchmarks/micro/MicroBenchmarks.csproj` - and this runs from the artifacts dir Then the running `MicroBenchmarks` tries to build the same project as a project reference from a generated wrapper project: 4. `// start dotnet restore`; `// start dotnet build ..` - And this emits warnings, and then errors: ``` warning MSB3026: Could not copy "/Users/radical/dev/performance/artifacts/obj/MicroBenchmarks/Release/net7.0/MicroBenchmarks.pdb" to "/Users/radical/dev/performance/artifacts/bin/MicroBenchmarks/Release/net7.0/MicroBenchmarks.pdb". Beginning retry 1 in 1000ms. The process cannot access the file '/Users/radical/dev/performance/artifacts/bin/MicroBenchmarks/Release/net7.0/MicroBenchmarks.pdb' because it is being used by another process. ``` - This is because the project is running from the same directory, as what the build in (4) tries to work with, which causes the build failure. If I use a different directory for (4), then there are no errors. This fails the build. But BDN ignores that, and runs the build again with `--no-dependencies`, which then skips the build of `MicroBenchmarks.csproj`, and just builds the wrapper project, thus succeeding. Solution: This commit prevents this by copying the build output to a separate directory, and running it from there. Fixes #2327 * Add a `--run-isolated` command line parameter This will cause the build output to be in `artifacts/bin/for-running/{project_name}`, instead of `artifacts/bin/{project_name}`. And then instead of using `dotnet run --project` to run, `dotnet exec {project_name.dll}` will be used. * Fix windows build - don't assume .dll extension * Use dotnet exec only when running isolated * clean up the api a bit * Bump bdn to 0.13.1.1740 * micro_benchmarks.py: add back '--' to the 'dotnet run' command line
1 parent 8852635 commit 2afd091

File tree

3 files changed

+86
-11
lines changed

3 files changed

+86
-11
lines changed

scripts/benchmarks_ci.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -230,9 +230,10 @@ def __main(args: list) -> int:
230230
# dotnet --info
231231
dotnet.info(verbose=verbose)
232232

233+
bin_dir_to_use=micro_benchmarks.get_bin_dir_to_use(args.csprojfile, args.bin_directory, args.run_isolated)
233234
BENCHMARKS_CSPROJ = dotnet.CSharpProject(
234235
project=args.csprojfile,
235-
bin_directory=args.bin_directory
236+
bin_directory=bin_dir_to_use
236237
)
237238

238239
if not args.run_only:
@@ -243,6 +244,7 @@ def __main(args: list) -> int:
243244
args.configuration,
244245
target_framework_monikers,
245246
args.incremental,
247+
args.run_isolated,
246248
verbose
247249
)
248250

@@ -255,6 +257,7 @@ def __main(args: list) -> int:
255257
BENCHMARKS_CSPROJ,
256258
args.configuration,
257259
framework,
260+
args.run_isolated,
258261
verbose,
259262
args
260263
)

scripts/dotnet.py

+34
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,18 @@ def info(verbose: bool) -> None:
3939
cmdline = ['dotnet', '--info']
4040
RunCommand(cmdline, verbose=verbose).run()
4141

42+
def exec(asm_path: str, verbose: bool, *args) -> None:
43+
"""
44+
Executes `dotnet exec` which can be used to execute assemblies
45+
"""
46+
asm_path=path.abspath(asm_path)
47+
working_dir=path.dirname(asm_path)
48+
if not path.exists(asm_path):
49+
raise ArgumentError('Cannot find assembly {} to exec'.format(asm_path))
50+
51+
cmdline = ['dotnet', 'exec', path.basename(asm_path)]
52+
cmdline += list(args)
53+
RunCommand(cmdline, verbose=verbose).run(working_dir)
4254

4355
def __log_script_header(message: str):
4456
message_length = len(message)
@@ -620,6 +632,28 @@ def get_commit_date(
620632
'Could not get timestamp for commit %s' % commit_sha)
621633
return build_timestamp
622634

635+
def get_project_name(csproj_file: str) -> str:
636+
'''
637+
Gets the project name from the csproj file path
638+
'''
639+
return path.splitext(path.basename(path.abspath(csproj_file)))[0]
640+
641+
def get_main_assembly_path(
642+
bin_directory: str,
643+
project_name: str) -> str:
644+
'''
645+
Gets the main assembly path, as {project_name}.dll, or .exe
646+
'''
647+
exe=path.join(bin_directory, project_name + '.exe')
648+
if path.exists(exe):
649+
return exe
650+
651+
dll=path.join(bin_directory, project_name + '.dll')
652+
if path.exists(dll):
653+
return dll
654+
655+
raise ValueError(
656+
'Unable to find main assembly - {} or {} in {}'.format(exe, dll, bin_directory))
623657

624658
def get_build_directory(
625659
bin_directory: str,

scripts/micro_benchmarks.py

+48-10
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,15 @@ def __get_bdn_arguments(user_input: str) -> list:
154154
'''harness.''',
155155
)
156156

157+
parser.add_argument(
158+
'--run-isolated',
159+
dest='run_isolated',
160+
required=False,
161+
default=False,
162+
action='store_true',
163+
help='Move the binaries to a different directory for running',
164+
)
165+
157166
def __valid_dir_path(file_path: str) -> str:
158167
'''Verifies that specified file path exists.'''
159168
file_path = path.abspath(file_path)
@@ -222,7 +231,7 @@ def __process_arguments(args: list) -> Tuple[list, bool]:
222231

223232

224233
def __get_benchmarkdotnet_arguments(framework: str, args: tuple) -> list:
225-
run_args = ['--']
234+
run_args = []
226235
if args.corerun:
227236
run_args += ['--coreRun'] + args.corerun
228237
if args.cli:
@@ -260,12 +269,21 @@ def __get_benchmarkdotnet_arguments(framework: str, args: tuple) -> list:
260269

261270
return run_args
262271

272+
def get_bin_dir_to_use(csprojfile: dotnet.CSharpProjFile, bin_directory: str, run_isolated: bool) -> str:
273+
'''
274+
Gets the bin_directory, which might be different if run_isolate=True
275+
'''
276+
if run_isolated:
277+
return path.join(bin_directory, 'for-running', dotnet.get_project_name(csprojfile.file_name))
278+
else:
279+
return bin_directory
263280

264281
def build(
265282
BENCHMARKS_CSPROJ: dotnet.CSharpProject,
266283
configuration: str,
267284
target_framework_monikers: list,
268285
incremental: str,
286+
run_isolated: bool,
269287
verbose: bool) -> None:
270288
'''Restores and builds the benchmarks'''
271289

@@ -291,32 +309,49 @@ def build(
291309
BENCHMARKS_CSPROJ.build(
292310
configuration=configuration,
293311
target_framework_monikers=target_framework_monikers,
312+
output_to_bindir=run_isolated,
294313
verbose=verbose,
295314
packages_path=packages)
296315

316+
# When running isolated, artifacts/obj/{project_name} will still be
317+
# there, and would interfere with any subsequent builds. So, remove
318+
# that
319+
if run_isolated:
320+
objDir = path.join(get_artifacts_directory(), 'obj', BENCHMARKS_CSPROJ.project_name)
321+
remove_directory(objDir)
297322

298323
def run(
299324
BENCHMARKS_CSPROJ: dotnet.CSharpProject,
300325
configuration: str,
301326
framework: str,
327+
run_isolated: bool,
302328
verbose: bool,
303329
*args) -> None:
304330
'''Runs the benchmarks'''
305331
__log_script_header("Running .NET micro benchmarks for '{}'".format(
306332
framework
307333
))
308-
# dotnet run
334+
335+
# dotnet exec
309336
run_args = __get_benchmarkdotnet_arguments(framework, *args)
310337
target_framework_moniker = dotnet.FrameworkAction.get_target_framework_moniker(
311338
framework
312339
)
313-
BENCHMARKS_CSPROJ.run(
314-
configuration,
315-
target_framework_moniker,
316-
verbose,
317-
*run_args
318-
)
319-
340+
341+
if run_isolated:
342+
runDir = BENCHMARKS_CSPROJ.bin_path
343+
asm_path=dotnet.get_main_assembly_path(runDir, BENCHMARKS_CSPROJ.project_name)
344+
dotnet.exec(asm_path, verbose, *run_args)
345+
else:
346+
# This is needed for `dotnet run`, but not for `dotnet exec`
347+
run_args = ['--'] + run_args
348+
BENCHMARKS_CSPROJ.run(
349+
configuration,
350+
target_framework_moniker,
351+
verbose,
352+
*run_args
353+
)
354+
320355
def __log_script_header(message: str):
321356
getLogger().info('-' * len(message))
322357
getLogger().info(message)
@@ -340,9 +375,10 @@ def __main(args: list) -> int:
340375
# dotnet --info
341376
dotnet.info(verbose)
342377

378+
bin_dir_to_use=micro_benchmarks.get_bin_dir_to_use(args.csprojfile, args.bin_directory, args.run_isolated)
343379
BENCHMARKS_CSPROJ = dotnet.CSharpProject(
344380
project=args.csprojfile,
345-
bin_directory=args.bin_directory
381+
bin_directory=bin_dir_to_use
346382
)
347383

348384
# dotnet build
@@ -351,6 +387,7 @@ def __main(args: list) -> int:
351387
configuration,
352388
target_framework_monikers,
353389
incremental,
390+
args.run_isolated,
354391
verbose
355392
)
356393

@@ -360,6 +397,7 @@ def __main(args: list) -> int:
360397
BENCHMARKS_CSPROJ,
361398
configuration,
362399
framework,
400+
args.run_isolated,
363401
verbose,
364402
args
365403
)

0 commit comments

Comments
 (0)