diff --git a/launcher-package/integration-test/src/test/scala/ExtendedRunnerTest.scala b/launcher-package/integration-test/src/test/scala/ExtendedRunnerTest.scala index 7ce9aedd4..69e312126 100755 --- a/launcher-package/integration-test/src/test/scala/ExtendedRunnerTest.scala +++ b/launcher-package/integration-test/src/test/scala/ExtendedRunnerTest.scala @@ -181,4 +181,54 @@ object ExtendedRunnerTest extends BasicTestSuite: } () } + + // Test for issue #8644: sbt.bat fails when project path contains parentheses + // https://github.com/sbt/sbt/issues/8644 + test("sbt.bat handles paths with parentheses") { + if (!isWindows) { + // This test is Windows-specific, skip on other platforms + () + } else { + IO.withTemporaryDirectory { baseDir => + // Create a temporary directory with parentheses in the name + val testDir = new File(baseDir, "test(parentheses)") + + // Create the directory structure + IO.createDirectory(testDir) + val projectDir = new File(testDir, "project") + IO.createDirectory(projectDir) + + // Create a minimal build.properties to make it a valid sbt project + val buildProps = new File(projectDir, "build.properties") + IO.write(buildProps, "sbt.version=1.12.1\n") + + // Test 1: Run sbt from directory with parentheses - should work without parsing errors + val out1 = sbtProcessInDir(testDir)("--script-version").!!.trim + val expectedVersion = "^" + versionRegEx + "$" + assert(out1.matches(expectedVersion), s"Expected version format, got: $out1") + + // Test 2: Test error message when no build.sbt exists (this is where the fix is most visible) + // Create a directory with parentheses but no build.sbt + val emptyDir = new File(baseDir, "empty(parentheses)") + IO.createDirectory(emptyDir) + + // Run sbt from empty directory - should fail gracefully with proper error message + // Use ProcessLogger to capture stderr without throwing on non-zero exit + import scala.sys.process.ProcessLogger + val errorBuffer = new StringBuilder + val logger = ProcessLogger( + _ => (), // ignore stdout + line => errorBuffer.append(line).append("\n") // capture stderr + ) + val exitCode = sbtProcessInDir(emptyDir)("compile").!(logger) + assert(exitCode == 1, "Expected sbt to fail when no build.sbt exists") + + // Verify the error output doesn't contain ") was unexpected" parsing error + val errorOutput = errorBuffer.toString + val hasParsingError = errorOutput.contains(") was unexpected") + assert(!hasParsingError, s"Error message should not contain parsing error when path has parentheses. Error output: $errorOutput") + } + } + () + } end ExtendedRunnerTest diff --git a/launcher-package/src/universal/bin/sbt.bat b/launcher-package/src/universal/bin/sbt.bat index 64091f484..867df3308 100755 --- a/launcher-package/src/universal/bin/sbt.bat +++ b/launcher-package/src/universal/bin/sbt.bat @@ -582,13 +582,16 @@ if exist project\build.properties ( set is_this_dir_sbt=1 ) +rem Store current directory in a variable for delayed expansion to handle paths with parentheses +set "CURRENT_DIR=%CD%" + rem Confirm a user's intent if the current directory does not look like an sbt rem top-level directory and the "new" command was not given. if not defined sbt_args_allow_empty if not defined sbt_args_print_version if not defined sbt_args_print_sbt_version if not defined sbt_args_print_sbt_script_version if not defined shutdownall ( if not !is_this_dir_sbt! equ 1 ( if not defined sbt_new ( - >&2 echo [error] Neither build.sbt nor a 'project' directory in the current directory: "%CD%" + >&2 echo [error] Neither build.sbt nor a 'project' directory in the current directory: "!CURRENT_DIR!" >&2 echo [error] run 'sbt new', touch build.sbt, or run 'sbt --allow-empty'. goto error )