1
0
Fork 0

Restore dev package extraction

New approach is to use only the solved set of packages as input and then
to resolve with only the non-dev requirements and to mark everything as
dev that is not part of the result set, rather than transitioning a
temporary local repo state by uninstalling dev packages.
pull/7936/head
Nils Adermann 2019-09-07 08:52:10 +02:00
parent 33ff67abf3
commit 3989a1b8ee
4 changed files with 168 additions and 19 deletions

View File

@ -33,6 +33,9 @@ class LocalRepoTransaction
/** @var RepositoryInterface */
protected $localRepository;
/**
* Reassigns ids for all packages in the lockedrepository
*/
public function __construct(RepositoryInterface $lockedRepository, $localRepository)
{
$this->localRepository = $localRepository;
@ -50,7 +53,10 @@ class LocalRepoTransaction
return strcmp($b->getName(), $a->getName());
};
$id = 1;
$this->lockedPackages = array();
foreach ($lockedRepository->getPackages() as $package) {
$package->id = $id++;
$this->lockedPackages[$package->id] = $package;
foreach ($package->getNames() as $name) {
$this->lockedPackagesByName[$name][] = $package;

View File

@ -16,7 +16,9 @@ use Composer\DependencyResolver\Operation\OperationInterface;
use Composer\Package\AliasPackage;
use Composer\Package\RootAliasPackage;
use Composer\Package\RootPackageInterface;
use Composer\Repository\ArrayRepository;
use Composer\Repository\RepositoryInterface;
use Composer\Test\Repository\ArrayRepositoryTest;
/**
* @author Nils Adermann <naderman@naderman.de>
@ -40,7 +42,8 @@ class LockTransaction
protected $unlockableMap;
protected $decisions;
protected $transaction;
protected $resultPackages;
public function __construct($policy, $pool, $presentMap, $unlockableMap, $decisions)
{
@ -104,31 +107,54 @@ class LockTransaction
}
}
$this->setResultPackages();
return $operations;
}
// TODO additionalFixedRepository needs to be looked at here as well?
public function getNewLockNonDevPackages()
// TODO make this a bit prettier instead of the two text indexes?
public function setResultPackages()
{
$packages = array();
$this->resultPackages = array('non-dev' => array(), 'dev' => array());
foreach ($this->decisions as $i => $decision) {
$literal = $decision[Decisions::DECISION_LITERAL];
if ($literal > 0) {
$package = $this->pool->literalToPackage($literal);
if (!isset($this->unlockableMap[$package->id]) && !($package instanceof AliasPackage) && !($package instanceof RootAliasPackage)) {
$packages[] = $package;
if (!isset($this->unlockableMap[$package->id])) {
$this->resultPackages['non-dev'][] = $package;
}
}
}
return $packages;
}
public function getNewLockDevPackages()
public function setNonDevPackages(LockTransaction $extractionResult)
{
$packages = $extractionResult->getNewLockPackages(false);
$this->resultPackages['dev'] = $this->resultPackages['non-dev'];
$this->resultPackages['non-dev'] = array();
foreach ($packages as $package) {
foreach ($this->resultPackages['dev'] as $i => $resultPackage) {
if ($package->getName() == $resultPackage->getName()) {
$this->resultPackages['non-dev'][] = $resultPackage;
unset($this->resultPackages['dev'][$i]);
}
}
}
}
// TODO additionalFixedRepository needs to be looked at here as well?
public function getNewLockPackages($devMode)
{
// TODO this is empty?
$packages = array();
foreach ($this->resultPackages[$devMode ? 'dev' : 'non-dev'] as $package) {
if (!($package instanceof AliasPackage) && !($package instanceof RootAliasPackage)) {
$packages[] = $package;
}
}
return $packages;
}

View File

@ -15,6 +15,7 @@ namespace Composer;
use Composer\Autoload\AutoloadGenerator;
use Composer\DependencyResolver\DefaultPolicy;
use Composer\DependencyResolver\LocalRepoTransaction;
use Composer\DependencyResolver\LockTransaction;
use Composer\DependencyResolver\Operation\UpdateOperation;
use Composer\DependencyResolver\Operation\InstallOperation;
use Composer\DependencyResolver\Operation\UninstallOperation;
@ -40,6 +41,7 @@ use Composer\Package\Link;
use Composer\Package\Loader\ArrayLoader;
use Composer\Package\Dumper\ArrayDumper;
use Composer\Package\Package;
use Composer\Repository\ArrayRepository;
use Composer\Repository\RepositorySet;
use Composer\Semver\Constraint\Constraint;
use Composer\Package\Locker;
@ -418,6 +420,8 @@ class Installer
$this->io->writeError('Nothing to modify in lock file');
}
$this->extractDevPackages($lockTransaction, $platformRepo, $aliases, $policy);
// write lock
$platformReqs = $this->extractPlatformRequirements($this->package->getRequires());
$platformDevReqs = $this->extractPlatformRequirements($this->package->getDevRequires());
@ -485,8 +489,8 @@ class Installer
}
$updatedLock = $this->locker->setLockData(
$lockTransaction->getNewLockNonDevPackages(),
$lockTransaction->getNewLockDevPackages(),
$lockTransaction->getNewLockPackages(false),
$lockTransaction->getNewLockPackages(true),
$platformReqs,
$platformDevReqs,
$aliases,
@ -509,6 +513,124 @@ class Installer
return 0;
}
/**
* Run the solver a second time on top of the existing update result with only the current result set in the pool
* and see what packages would get removed if we only had the non-dev packages in the solver request
*/
protected function extractDevPackages(LockTransaction $lockTransaction, $platformRepo, $aliases, $policy)
{
if (!$this->package->getDevRequires()) {
return array();
}
;
$resultRepo = new ArrayRepository(array());
$loader = new ArrayLoader(null, true);
$dumper = new ArrayDumper();
foreach ($lockTransaction->getNewLockPackages(false) as $pkg) {
$resultRepo->addPackage($loader->load($dumper->dump($pkg)));
}
$repositorySet = $this->createRepositorySet($platformRepo, $aliases, null);
$repositorySet->addRepository($resultRepo);
$request = $this->createRequest($this->fixedRootPackage, $platformRepo, null);
$links = $this->package->getRequires();
foreach ($links as $link) {
$request->install($link->getTarget(), $link->getConstraint());
}
$pool = $repositorySet->createPool($request);
// solve dependencies
$solver = new Solver($policy, $pool, $this->io);
try {
$nonDevLockTransaction = $solver->solve($request, $this->ignorePlatformReqs);
$solver = null;
} catch (SolverProblemsException $e) {
// TODO change info message here
$this->io->writeError('<error>Your requirements could not be resolved to an installable set of packages.</error>', true, IOInterface::QUIET);
$this->io->writeError($e->getMessage());
return max(1, $e->getCode());
}
$lockTransaction->setNonDevPackages($nonDevLockTransaction);
}
// TODO add proper output and events to above function based on old version below
/**
* Extracts the dev packages out of the localRepo
*
* This works by faking the operations so we can see what the dev packages
* would be at the end of the operation execution. This lets us then remove
* the dev packages from the list of operations accordingly if we are in a
* --no-dev install or update.
*
* @return array
private function extractDevPackages(array $operations, RepositoryInterface $localRepo, PlatformRepository $platformRepo, array $aliases)
{
// fake-apply all operations to this clone of the local repo so we see the complete set of package we would end up with
$tempLocalRepo = clone $localRepo;
foreach ($operations as $operation) {
switch ($operation->getJobType()) {
case 'install':
case 'markAliasInstalled':
if (!$tempLocalRepo->hasPackage($operation->getPackage())) {
$tempLocalRepo->addPackage(clone $operation->getPackage());
}
break;
case 'uninstall':
case 'markAliasUninstalled':
$tempLocalRepo->removePackage($operation->getPackage());
break;
case 'update':
$tempLocalRepo->removePackage($operation->getInitialPackage());
if (!$tempLocalRepo->hasPackage($operation->getTargetPackage())) {
$tempLocalRepo->addPackage(clone $operation->getTargetPackage());
}
break;
default:
throw new \LogicException('Unknown type: '.$operation->getJobType());
}
}
// we have to reload the local repo to handle aliases properly
// but as it is not persisted on disk we use a loader/dumper
// to reload it in memory
$localRepo = new InstalledArrayRepository(array());
$loader = new ArrayLoader(null, true);
$dumper = new ArrayDumper();
foreach ($tempLocalRepo->getCanonicalPackages() as $pkg) {
$localRepo->addPackage($loader->load($dumper->dump($pkg)));
}
unset($tempLocalRepo, $loader, $dumper);
$policy = $this->createPolicy();
$pool = $this->createPool();
$installedRepo = $this->createInstalledRepo($localRepo, $platformRepo);
$pool->addRepository($installedRepo, $aliases);
// creating requirements request without dev requirements
$request = $this->createRequest($this->package, $platformRepo);
$request->updateAll();
foreach ($this->package->getRequires() as $link) {
$request->install($link->getTarget(), $link->getConstraint());
}
// solve deps to see which get removed
$this->eventDispatcher->dispatchInstallerEvent(InstallerEvents::PRE_DEPENDENCIES_SOLVING, false, $policy, $pool, $installedRepo, $request);
$solver = new Solver($policy, $pool, $installedRepo, $this->io);
$ops = $solver->solve($request, $this->ignorePlatformReqs);
$this->eventDispatcher->dispatchInstallerEvent(InstallerEvents::POST_DEPENDENCIES_SOLVING, false, $policy, $pool, $installedRepo, $request, $ops);
$devPackages = array();
foreach ($ops as $op) {
if ($op->getJobType() === 'uninstall') {
$devPackages[] = $op->getPackage();
}
}
return $devPackages;
}*/
/**
* @param RepositoryInterface $localRepo
* @param RepositoryInterface $installedRepo
@ -575,12 +697,6 @@ class Installer
// TODO should we warn people / error if plugins in vendor folder do not match contents of lock file before update?
//$this->eventDispatcher->dispatchInstallerEvent(InstallerEvents::POST_DEPENDENCIES_SOLVING, $this->devMode, $policy, $repositorySet, $installedRepo, $request, $lockTransaction);
} else {
// we still need to ensure all packages have an id for correct functionality
$id = 1;
foreach ($lockedRepository->getPackages() as $package) {
$package->id = $id++;
}
}
// TODO in how far do we need to do anything here to ensure dev packages being updated to latest in lock without version change are treated correctly?
@ -672,6 +788,7 @@ class Installer
// TODO what's the point of rootConstraints at all, we generate the package pool taking them into account anyway?
// TODO maybe we can drop the lockedRepository here
// TODO if this gets called in doInstall, this->update is still true?!
if ($this->update) {
$minimumStability = $this->package->getMinimumStability();
$stabilityFlags = $this->package->getStabilityFlags();

View File

@ -62,7 +62,7 @@ update --no-dev
--EXPECT--
Uninstalling a/b (1.0.0)
Updating a/a (1.0.0) to a/a (1.0.1)
Updating dev/pkg (dev-master old) to dev/pkg (dev-master new)
Installing a/c (1.0.0)
Updating dev/pkg (dev-master old) to dev/pkg (dev-master new)
Marking dev/pkg (1.1.x-dev new) as installed, alias of dev/pkg (dev-master new)
Marking dev/pkg (1.0.x-dev old) as uninstalled, alias of dev/pkg (dev-master old)