diff --git a/src/Composer/Autoload/AutoloadGenerator.php b/src/Composer/Autoload/AutoloadGenerator.php index 8627552bb..e3e5c49a9 100644 --- a/src/Composer/Autoload/AutoloadGenerator.php +++ b/src/Composer/Autoload/AutoloadGenerator.php @@ -17,6 +17,7 @@ use Composer\Json\JsonFile; use Composer\Package\Loader\JsonLoader; use Composer\Package\PackageInterface; use Composer\Repository\RepositoryInterface; +use Composer\Downloader\Util\Filesystem; /** * @author Igor Wiedler @@ -27,7 +28,6 @@ class AutoloadGenerator public function dump(RepositoryInterface $localRepo, PackageInterface $mainPackage, InstallationManager $installationManager, $targetDir) { $autoloadFile = file_get_contents(__DIR__.'/ClassLoader.php'); - $autoloadFile .= <<<'EOF' // autoload.php generated by Composer @@ -49,12 +49,17 @@ function init() { return init(); EOF; - $namespacesFile = <<<'EOF' + $filesystem = new Filesystem(); + $vendorPath = strtr(realpath($installationManager->getVendorPath()), '\\', '/'); + $relVendorPath = ltrim(substr($vendorPath, strlen(getcwd())), '/'); + $vendorDirCode = $filesystem->findShortestPathCode(realpath($targetDir), $vendorPath, true); + + $namespacesFile = <<parseAutoloads($packageMap); - $vendorPath = $installationManager->getVendorPath(); - $realVendorPath = realpath($vendorPath); - $vendorPathDepth = substr_count(strtr(substr($realVendorPath, strlen(getcwd())), '\\', '/'), '/'); - $appBaseDir = str_repeat('dirname(', $vendorPathDepth).'$baseDir'.str_repeat(')', $vendorPathDepth); + $appBaseDir = $filesystem->findShortestPathCode($vendorPath, getcwd(), true); + $appBaseDir = str_replace('__DIR__', '$vendorDir', $appBaseDir); if (isset($autoloads['psr-0'])) { foreach ($autoloads['psr-0'] as $def) { - if (!$this->isAbsolutePath($def['path'])) { - if (strpos($def['path'], $vendorPath) === 0) { - $def['path'] = substr($def['path'], strlen($vendorPath)); - $baseDir = '$baseDir . '; + $def['path'] = strtr($def['path'], '\\', '/'); + $baseDir = ''; + if (!$filesystem->isAbsolutePath($def['path'])) { + if (strpos($def['path'], $relVendorPath) === 0) { + $def['path'] = substr($def['path'], strlen($relVendorPath)); + $baseDir = '$vendorDir . '; } else { $def['path'] = '/'.$def['path']; $baseDir = $appBaseDir . ' . '; } - } else { - $baseDir = ''; + } elseif (strpos($def['path'], $vendorPath) === 0) { + $def['path'] = substr($def['path'], strlen($vendorPath)); + $baseDir = '$vendorDir . '; } $exportedPrefix = var_export($def['namespace'], true); $exportedPath = var_export($def['path'], true); @@ -158,9 +163,4 @@ EOF; return $loader; } - - protected function isAbsolutePath($path) - { - return substr($path, 0, 1) === '/' || substr($path, 1, 1) === ':'; - } } diff --git a/src/Composer/Downloader/Util/Filesystem.php b/src/Composer/Downloader/Util/Filesystem.php index 66e423e1d..e486d8631 100644 --- a/src/Composer/Downloader/Util/Filesystem.php +++ b/src/Composer/Downloader/Util/Filesystem.php @@ -58,22 +58,22 @@ class Filesystem if (dirname($from) === dirname($to)) { return './'.basename($to); } - $from = strtr($from, '\\', '/'); - $to = strtr($to, '\\', '/'); + $from = rtrim(strtr($from, '\\', '/'), '/'); + $to = rtrim(strtr($to, '\\', '/'), '/'); - $commonPath = dirname($to); - while (strpos($from, $commonPath) !== 0 && '/' !== $commonPath && !preg_match('{^[a-z]:/$}i', $commonPath)) { + $commonPath = $to; + while (strpos($from, $commonPath) !== 0 && '/' !== $commonPath && !preg_match('{^[a-z]:/?$}i', $commonPath) && '.' !== $commonPath) { $commonPath = strtr(dirname($commonPath), '\\', '/'); } - if (0 !== strpos($from, $commonPath) || '/' === $commonPath) { + if (0 !== strpos($from, $commonPath) || '/' === $commonPath || '.' === $commonPath) { return $to; } $commonPath = rtrim($commonPath, '/') . '/'; $sourcePathDepth = substr_count(substr($from, strlen($commonPath)), '/'); $commonPathCode = str_repeat('../', $sourcePathDepth); - return $commonPathCode . substr($to, strlen($commonPath)); + return ($commonPathCode . substr($to, strlen($commonPath))) ?: './'; } /** @@ -81,33 +81,38 @@ class Filesystem * * @param string $from * @param string $to + * @param Boolean $directories if true, the source/target are considered to be directories * @return string */ - public function findShortestPathCode($from, $to) + public function findShortestPathCode($from, $to, $directories = false) { if (!$this->isAbsolutePath($from) || !$this->isAbsolutePath($to)) { throw new \InvalidArgumentException('from and to must be absolute paths'); } if ($from === $to) { - return '__FILE__'; + return $directories ? '__DIR__' : '__FILE__'; } $from = strtr($from, '\\', '/'); $to = strtr($to, '\\', '/'); - $commonPath = dirname($to); - while (strpos($from, $commonPath) !== 0 && '/' !== $commonPath && !preg_match('{^[a-z]:/$}i', $commonPath)) { + $commonPath = $to; + while (strpos($from, $commonPath) !== 0 && '/' !== $commonPath && !preg_match('{^[a-z]:/?$}i', $commonPath) && '.' !== $commonPath) { $commonPath = strtr(dirname($commonPath), '\\', '/'); } - if (0 !== strpos($from, $commonPath) || '/' === $commonPath) { + if (0 !== strpos($from, $commonPath) || '/' === $commonPath || '.' === $commonPath) { return var_export($to, true); } $commonPath = rtrim($commonPath, '/') . '/'; - $sourcePathDepth = substr_count(substr($from, strlen($commonPath)), '/'); + if (strpos($to, $from.'/') === 0) { + return '__DIR__ . '.var_export(substr($to, strlen($from)), true); + } + $sourcePathDepth = substr_count(substr($from, strlen($commonPath)), '/') + $directories; $commonPathCode = str_repeat('dirname(', $sourcePathDepth).'__DIR__'.str_repeat(')', $sourcePathDepth); - return $commonPathCode . '.' . var_export('/' . substr($to, strlen($commonPath)), true); + $relTarget = substr($to, strlen($commonPath)); + return $commonPathCode . (strlen($relTarget) ? '.' . var_export('/' . $relTarget, true) : ''); } /** diff --git a/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php b/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php new file mode 100644 index 000000000..d1236d6f0 --- /dev/null +++ b/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php @@ -0,0 +1,118 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Test\Installer; + +use Composer\Autoload\AutoloadGenerator; +use Composer\Downloader\Util\Filesystem; +use Composer\Package\MemoryPackage; + +class AutoloadGeneratorTest extends \PHPUnit_Framework_TestCase +{ + public $vendorDir; + private $workingDir; + private $im; + private $repository; + private $generator; + + protected function setUp() + { + $fs = new Filesystem; + $that = $this; + + $this->workingDir = sys_get_temp_dir(); + $this->vendorDir = $this->workingDir.DIRECTORY_SEPARATOR.'composer-test-autoload'; + if (is_dir($this->vendorDir)) { + $fs->removeDirectory($this->vendorDir); + } + mkdir($this->vendorDir); + + $this->dir = getcwd(); + chdir($this->workingDir); + + $this->im = $this->getMockBuilder('Composer\Installer\InstallationManager') + ->disableOriginalConstructor() + ->getMock(); + $this->im->expects($this->any()) + ->method('getInstallPath') + ->will($this->returnCallback(function ($package) use ($that) { + return $that->vendorDir.'/'.$package->getName(); + })); + $this->im->expects($this->any()) + ->method('getVendorPath') + ->will($this->returnCallback(function () use ($that) { + return $that->vendorDir; + })); + + $this->repo = $this->getMock('Composer\Repository\RepositoryInterface'); + + $this->generator = new AutoloadGenerator(); + } + + protected function tearDown() + { + chdir($this->dir); + } + + public function testMainPackageAutoloading() + { + $package = new MemoryPackage('a', '1.0', '1.0'); + $package->setAutoload(array('psr-0' => array('Main' => 'src/', 'Lala' => 'src/'))); + + $this->repo->expects($this->once()) + ->method('getPackages') + ->will($this->returnValue(array())); + + mkdir($this->vendorDir.'/.composer'); + $this->generator->dump($this->repo, $package, $this->im, $this->vendorDir.'/.composer'); + $this->assertAutoloadFiles('main', $this->vendorDir.'/.composer'); + } + + public function testMainPackageAutoloadingAlternativeVendorDir() + { + $package = new MemoryPackage('a', '1.0', '1.0'); + $package->setAutoload(array('psr-0' => array('Main' => 'src/', 'Lala' => 'src/'))); + + $this->repo->expects($this->once()) + ->method('getPackages') + ->will($this->returnValue(array())); + + $this->vendorDir .= '/subdir'; + mkdir($this->vendorDir.'/.composer', 0777, true); + $this->generator->dump($this->repo, $package, $this->im, $this->vendorDir.'/.composer'); + $this->assertAutoloadFiles('main2', $this->vendorDir.'/.composer'); + } + + public function testVendorsAutoloading() + { + $package = new MemoryPackage('a', '1.0', '1.0'); + + $packages = array(); + $packages[] = $a = new MemoryPackage('a/a', '1.0', '1.0'); + $packages[] = $b = new MemoryPackage('b/b', '1.0', '1.0'); + $a->setAutoload(array('psr-0' => array('A' => 'src/', 'A\\B' => 'lib/'))); + $b->setAutoload(array('psr-0' => array('B\\Sub\\Name' => 'src/'))); + + $this->repo->expects($this->once()) + ->method('getPackages') + ->will($this->returnValue($packages)); + + mkdir($this->vendorDir.'/.composer', 0777, true); + $this->generator->dump($this->repo, $package, $this->im, $this->vendorDir.'/.composer'); + $this->assertAutoloadFiles('vendors', $this->vendorDir.'/.composer'); + } + + private function assertAutoloadFiles($name, $dir) + { + $this->assertFileEquals(__DIR__.'/Fixtures/autoload_'.$name.'.php', $dir.'/autoload_namespaces.php'); + } +} diff --git a/tests/Composer/Test/Autoload/Fixtures/autoload_main.php b/tests/Composer/Test/Autoload/Fixtures/autoload_main.php new file mode 100644 index 000000000..157d888f3 --- /dev/null +++ b/tests/Composer/Test/Autoload/Fixtures/autoload_main.php @@ -0,0 +1,10 @@ + dirname($vendorDir) . '/src/', + 'Lala' => dirname($vendorDir) . '/src/', +); diff --git a/tests/Composer/Test/Autoload/Fixtures/autoload_main2.php b/tests/Composer/Test/Autoload/Fixtures/autoload_main2.php new file mode 100644 index 000000000..a0fa2c7db --- /dev/null +++ b/tests/Composer/Test/Autoload/Fixtures/autoload_main2.php @@ -0,0 +1,10 @@ + dirname(dirname($vendorDir)) . '/src/', + 'Lala' => dirname(dirname($vendorDir)) . '/src/', +); diff --git a/tests/Composer/Test/Autoload/Fixtures/autoload_vendors.php b/tests/Composer/Test/Autoload/Fixtures/autoload_vendors.php new file mode 100644 index 000000000..b149046df --- /dev/null +++ b/tests/Composer/Test/Autoload/Fixtures/autoload_vendors.php @@ -0,0 +1,11 @@ + $vendorDir . '/b/b/src/', + 'A\\B' => $vendorDir . '/a/a/lib/', + 'A' => $vendorDir . '/a/a/src/', +); diff --git a/tests/Composer/Test/Downloader/Util/FilesystemTest.php b/tests/Composer/Test/Downloader/Util/FilesystemTest.php index f4d9af04f..01a3189b8 100644 --- a/tests/Composer/Test/Downloader/Util/FilesystemTest.php +++ b/tests/Composer/Test/Downloader/Util/FilesystemTest.php @@ -20,24 +20,37 @@ class FilesystemTest extends TestCase /** * @dataProvider providePathCouplesAsCode */ - public function testFindShortestPathCode($a, $b, $expected) + public function testFindShortestPathCode($a, $b, $directory, $expected) { $fs = new Filesystem; - $this->assertEquals($expected, $fs->findShortestPathCode($a, $b)); + $this->assertEquals($expected, $fs->findShortestPathCode($a, $b, $directory)); } public function providePathCouplesAsCode() { return array( - array('/foo/bar', '/foo/bar', "__FILE__"), - array('/foo/bar', '/foo/baz', "__DIR__.'/baz'"), - array('/foo/bin/run', '/foo/vendor/acme/bin/run', "dirname(__DIR__).'/vendor/acme/bin/run'"), - array('/foo/bin/run', '/foo/vendor/acme/bin/run', "dirname(__DIR__).'/vendor/acme/bin/run'"), - array('/foo/bin/run', '/bar/bin/run', "'/bar/bin/run'"), - array('c:/bin/run', 'c:/vendor/acme/bin/run', "dirname(__DIR__).'/vendor/acme/bin/run'"), - array('c:\\bin\\run', 'c:/vendor/acme/bin/run', "dirname(__DIR__).'/vendor/acme/bin/run'"), - array('c:/bin/run', 'd:/vendor/acme/bin/run', "'d:/vendor/acme/bin/run'"), - array('c:\\bin\\run', 'd:/vendor/acme/bin/run', "'d:/vendor/acme/bin/run'"), + array('/foo/bar', '/foo/bar', false, "__FILE__"), + array('/foo/bar', '/foo/baz', false, "__DIR__.'/baz'"), + array('/foo/bin/run', '/foo/vendor/acme/bin/run', false, "dirname(__DIR__).'/vendor/acme/bin/run'"), + array('/foo/bin/run', '/foo/vendor/acme/bin/run', false, "dirname(__DIR__).'/vendor/acme/bin/run'"), + array('/foo/bin/run', '/bar/bin/run', false, "'/bar/bin/run'"), + array('c:/bin/run', 'c:/vendor/acme/bin/run', false, "dirname(__DIR__).'/vendor/acme/bin/run'"), + array('c:\\bin\\run', 'c:/vendor/acme/bin/run', false, "dirname(__DIR__).'/vendor/acme/bin/run'"), + array('c:/bin/run', 'd:/vendor/acme/bin/run', false, "'d:/vendor/acme/bin/run'"), + array('c:\\bin\\run', 'd:/vendor/acme/bin/run', false, "'d:/vendor/acme/bin/run'"), + array('/foo/bar', '/foo/bar', true, "__DIR__"), + array('/foo/bar', '/foo/baz', true, "dirname(__DIR__).'/baz'"), + array('/foo/bin/run', '/foo/vendor/acme/bin/run', true, "dirname(dirname(__DIR__)).'/vendor/acme/bin/run'"), + array('/foo/bin/run', '/foo/vendor/acme/bin/run', true, "dirname(dirname(__DIR__)).'/vendor/acme/bin/run'"), + array('/foo/bin/run', '/bar/bin/run', true, "'/bar/bin/run'"), + array('c:/bin/run', 'c:/vendor/acme/bin/run', true, "dirname(dirname(__DIR__)).'/vendor/acme/bin/run'"), + array('c:\\bin\\run', 'c:/vendor/acme/bin/run', true, "dirname(dirname(__DIR__)).'/vendor/acme/bin/run'"), + array('c:/bin/run', 'd:/vendor/acme/bin/run', true, "'d:/vendor/acme/bin/run'"), + array('c:\\bin\\run', 'd:/vendor/acme/bin/run', true, "'d:/vendor/acme/bin/run'"), + array('C:/Temp/test', 'C:\Temp', true, "dirname(__DIR__)"), + array('C:/Temp', 'C:\Temp\test', true, "__DIR__ . '/test'"), + array('/tmp/test', '/tmp', true, "dirname(__DIR__)"), + array('/tmp', '/tmp/test', true, "__DIR__ . '/test'"), ); } @@ -62,6 +75,12 @@ class FilesystemTest extends TestCase array('c:\\bin\\run', 'c:/vendor/acme/bin/run', "../vendor/acme/bin/run"), array('c:/bin/run', 'd:/vendor/acme/bin/run', "d:/vendor/acme/bin/run"), array('c:\\bin\\run', 'd:/vendor/acme/bin/run', "d:/vendor/acme/bin/run"), + array('C:/Temp/test', 'C:\Temp', "./"), + array('/tmp/test', '/tmp', "./"), + array('C:/Temp/test/sub', 'C:\Temp', "../"), + array('/tmp/test/sub', '/tmp', "../"), + array('/tmp', '/tmp/test', "test"), + array('C:/Temp', 'C:\Temp\test', "test"), ); } }