[2.x] fix: Handle paths with parentheses in sbt.bat on Windows (#8656)

Windows CMD interprets parentheses as special syntax for command grouping.
When the project directory path contains parentheses (e.g., in username),
the batch script fails with ') was unexpected at this time.' error.

This fix stores the current directory in a variable using delayed expansion
(!CURRENT_DIR!) instead of using %CD% directly, which properly handles
paths containing parentheses and other special characters.

Fixes #8644
This commit is contained in:
PandaMan 2026-01-30 05:51:06 +08:00 committed by GitHub
parent f05a96aceb
commit 461e12d497
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 54 additions and 1 deletions

View File

@ -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

View File

@ -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
)