diff --git a/src/filesystem/FileFinder.php b/src/filesystem/FileFinder.php --- a/src/filesystem/FileFinder.php +++ b/src/filesystem/FileFinder.php @@ -35,7 +35,9 @@ * @task create */ public function __construct($root) { - $this->root = rtrim($root, '/'); + $this->root = rtrim( + $root, + phutil_is_windows() ? getenv('HOMEDRIVE') : '/'); } /** diff --git a/src/filesystem/Filesystem.php b/src/filesystem/Filesystem.php --- a/src/filesystem/Filesystem.php +++ b/src/filesystem/Filesystem.php @@ -610,6 +610,17 @@ /* -( Directories )-------------------------------------------------------- */ + /** + * Build a path from the pieces provided using the system's directory + * separator. + * + * @param ...string The list of directory paths to be joined + * @return string The built, full directory path. + */ + public static function buildPath() { + return implode(DIRECTORY_SEPARATOR, func_get_args()); + } + /** * Create a directory in a manner similar to mkdir(), but throw detailed @@ -766,6 +777,24 @@ * @task directory */ public static function walkToRoot($path, $root = '/') { + if (phutil_is_windows()) { + $win_root = getenv('HOMEDRIVE'); + + $root = explode('/', $root); + $path = explode('/', $path); + + if (!$root[0]) { + $root[0] = $win_root; + } + + if (!$path[0]) { + $path[0] = $win_root; + } + + $root = implode(DIRECTORY_SEPARATOR, $root); + $path = implode(DIRECTORY_SEPARATOR, $path); + } + $path = self::resolvePath($path); $root = self::resolvePath($root); @@ -798,7 +827,10 @@ $next = DIRECTORY_SEPARATOR.implode(DIRECTORY_SEPARATOR, $parts); } - $walk[] = $next; + if ($next) { + $walk[] = $next; + } + if ($next == $root) { break; } diff --git a/src/filesystem/__tests__/FileFinderTestCase.php b/src/filesystem/__tests__/FileFinderTestCase.php --- a/src/filesystem/__tests__/FileFinderTestCase.php +++ b/src/filesystem/__tests__/FileFinderTestCase.php @@ -9,8 +9,12 @@ return $finder; } + protected function getModes() { + return phutil_is_windows()? array('php') : array('php', 'shell'); + } + public function testFinderWithChecksums() { - foreach (array('php', 'shell') as $mode) { + foreach ($this->getModes() as $mode) { $files = $this->getFinder() ->setGenerateChecksums(true) ->withType('f') @@ -52,7 +56,7 @@ } public function testFinderWithoutChecksums() { - foreach (array('php', 'shell') as $mode) { + foreach ($this->getModes() as $mode) { $files = $this->getFinder() ->withType('f') ->withPath('*') @@ -77,7 +81,7 @@ } public function testFinderWithDirectories() { - foreach (array('php', 'shell') as $mode) { + foreach ($this->getModes() as $mode) { $files = $this->getFinder() ->setGenerateChecksums(true) ->withPath('*') @@ -105,7 +109,7 @@ } public function testFinderWithPath() { - foreach (array('php', 'shell') as $mode) { + foreach ($this->getModes() as $mode) { $files = $this->getFinder() ->setGenerateChecksums(true) ->withType('f') @@ -125,7 +129,7 @@ } public function testFinderWithNames() { - foreach (array('php', 'shell') as $mode) { + foreach ($this->getModes() as $mode) { $files = $this->getFinder() ->withType('f') ->withPath('*') diff --git a/src/filesystem/__tests__/FilesystemTestCase.php b/src/filesystem/__tests__/FilesystemTestCase.php --- a/src/filesystem/__tests__/FilesystemTestCase.php +++ b/src/filesystem/__tests__/FilesystemTestCase.php @@ -74,25 +74,36 @@ } public function testWalkToRoot() { + $win_root = phutil_is_windows() ? getenv('HOMEDRIVE') : ''; $test_cases = array( array( - dirname(__FILE__).'/data/include_dir.txt/subdir.txt/test', + Filesystem::buildPath( + dirname(__FILE__), + 'data', 'include_dir.txt', 'subdir.txt', 'test'), dirname(__FILE__), array( - dirname(__FILE__).'/data/include_dir.txt/subdir.txt/test', - dirname(__FILE__).'/data/include_dir.txt/subdir.txt', - dirname(__FILE__).'/data/include_dir.txt', - dirname(__FILE__).'/data', + Filesystem::buildPath( + dirname(__FILE__), + 'data', 'include_dir.txt', 'subdir.txt', 'test'), + Filesystem::buildPath( + dirname(__FILE__), + 'data', 'include_dir.txt', 'subdir.txt'), + Filesystem::buildPath(dirname(__FILE__), 'data', 'include_dir.txt'), + Filesystem::buildPath(dirname(__FILE__), 'data'), dirname(__FILE__), ), ), array( - dirname(__FILE__).'/data/include_dir.txt/subdir.txt', + Filesystem::buildPath( + dirname(__FILE__), + 'data', 'include_dir.txt', 'subdir.txt'), dirname(__FILE__), array( - dirname(__FILE__).'/data/include_dir.txt/subdir.txt', - dirname(__FILE__).'/data/include_dir.txt', - dirname(__FILE__).'/data', + Filesystem::buildPath( + dirname(__FILE__), + 'data', 'include_dir.txt', 'subdir.txt'), + Filesystem::buildPath(dirname(__FILE__), 'data', 'include_dir.txt'), + Filesystem::buildPath(dirname(__FILE__), 'data'), dirname(__FILE__), ), ), @@ -107,19 +118,28 @@ 'root is not an ancestor of path' => array( dirname(__FILE__), - dirname(__FILE__).'/data/include_dir.txt/subdir.txt', + Filesystem::buildPath( + dirname(__FILE__), 'data', 'include_dir.txt', 'subdir.txt'), array(), ), 'fictional paths work' => array( '/x/y/z', '/', - array( - '/x/y/z', - '/x/y', - '/x', - '/', - ), + phutil_is_windows() ? + array( + $win_root.'\\x\\y\\z', + $win_root.'\\x\\y', + $win_root.'\\x', + $win_root, + ) + : + array( + '/x/y/z', + '/x/y', + '/x', + '/', + ), ), ); diff --git a/src/filesystem/__tests__/PhutilDeferredLogTestCase.php b/src/filesystem/__tests__/PhutilDeferredLogTestCase.php --- a/src/filesystem/__tests__/PhutilDeferredLogTestCase.php +++ b/src/filesystem/__tests__/PhutilDeferredLogTestCase.php @@ -105,7 +105,7 @@ $futures = array(); for ($ii = 0; $ii < $n_writers; $ii++) { - $futures[] = new ExecFuture('%s %d %s', $bin, $n_lines, (string)$tmp); + $futures[] = new ExecFuture('php %s %d %s', $bin, $n_lines, (string)$tmp); } id(new FutureIterator($futures)) diff --git a/src/filesystem/__tests__/PhutilFileLockTestCase.php b/src/filesystem/__tests__/PhutilFileLockTestCase.php --- a/src/filesystem/__tests__/PhutilFileLockTestCase.php +++ b/src/filesystem/__tests__/PhutilFileLockTestCase.php @@ -148,6 +148,12 @@ } private function holdLock($file) { + if (phutil_is_windows()) { + // Skip since stdout is not reliable on Windows + PHP. + $this->assertSkipped(pht("This test doesn't work on Windows.")); + return; + } + $future = $this->buildLockFuture('--hold', $file); // We can't return until we're sure the subprocess has had time to acquire @@ -174,9 +180,14 @@ $root = dirname(phutil_get_library_root('phutil')); $bin = $root.'/scripts/utils/lock.php'; - // NOTE: Use `exec` so this passes on Ubuntu, where the default `dash` shell - // will eat any kills we send during the tests. - $future = new ExecFuture('exec php %s %C %s', $bin, $flags, $file); + if (phutil_is_windows()) { + $future = new ExecFuture('php %s %C %s', $bin, $flags, $file); + } else { + // NOTE: Use `exec` so this passes on Ubuntu, + // where the default `dash` shell will eat any + // kills we send during the tests. + $future = new ExecFuture('exec php %s %C %s', $bin, $flags, $file); + } $future->start(); return $future; }