Differential D7872 Diff 17810 src/infrastructure/celerity/management/CelerityManagementMapWorkflow.php
Changeset View
Changeset View
Standalone View
Standalone View
src/infrastructure/celerity/management/CelerityManagementMapWorkflow.php
Show All 9 Lines | $this | ||||
->setSynopsis(pht('Rebuild static resource maps.')) | ->setSynopsis(pht('Rebuild static resource maps.')) | ||||
->setArguments( | ->setArguments( | ||||
array()); | array()); | ||||
} | } | ||||
public function execute(PhutilArgumentParser $args) { | public function execute(PhutilArgumentParser $args) { | ||||
$resources_map = CelerityResources::getAll(); | $resources_map = CelerityResources::getAll(); | ||||
$this->log( | |||||
pht( | |||||
"Rebuilding %d resource source(s).", | |||||
new PhutilNumber(count($resources_map)))); | |||||
foreach ($resources_map as $name => $resources) { | foreach ($resources_map as $name => $resources) { | ||||
$this->rebuildResources($resources); | $this->rebuildResources($resources); | ||||
} | } | ||||
$this->log(pht("Done.")); | |||||
return 0; | return 0; | ||||
} | } | ||||
/** | /** | ||||
* Rebuild the resource map for a resource source. | * Rebuild the resource map for a resource source. | ||||
* | * | ||||
* @param CelerityResources Resource source to rebuild. | * @param CelerityResources Resource source to rebuild. | ||||
* @return void | * @return void | ||||
*/ | */ | ||||
private function rebuildResources(CelerityResources $resources) { | private function rebuildResources(CelerityResources $resources) { | ||||
$this->log( | |||||
pht( | |||||
'Rebuilding resource source "%s" (%s)...', | |||||
$resources->getName(), | |||||
get_class($resources))); | |||||
$binary_map = $this->rebuildBinaryResources($resources); | $binary_map = $this->rebuildBinaryResources($resources); | ||||
$this->log( | |||||
pht( | |||||
'Found %d binary resources.', | |||||
new PhutilNumber(count($binary_map)))); | |||||
$xformer = id(new CelerityResourceTransformer()) | $xformer = id(new CelerityResourceTransformer()) | ||||
->setMinify(false) | ->setMinify(false) | ||||
->setRawURIMap(ipull($binary_map, 'uri')); | ->setRawURIMap(ipull($binary_map, 'uri')); | ||||
$text_map = $this->rebuildTextResources($resources, $xformer); | $text_map = $this->rebuildTextResources($resources, $xformer); | ||||
$this->log( | |||||
pht( | |||||
'Found %d text resources.', | |||||
new PhutilNumber(count($text_map)))); | |||||
$resource_graph = array(); | $resource_graph = array(); | ||||
$requires_map = array(); | $requires_map = array(); | ||||
$provides_map = array(); | $symbol_map = array(); | ||||
foreach ($text_map as $name => $info) { | foreach ($text_map as $name => $info) { | ||||
if (isset($info['provides'])) { | if (isset($info['provides'])) { | ||||
$provides_map[$info['provides']] = $info['hash']; | $symbol_map[$info['provides']] = $info['hash']; | ||||
// We only need to check for cycles and add this to the requires map | // We only need to check for cycles and add this to the requires map | ||||
// if it actually requires anything. | // if it actually requires anything. | ||||
if (!empty($info['requires'])) { | if (!empty($info['requires'])) { | ||||
$resource_graph[$info['provides']] = $info['requires']; | $resource_graph[$info['provides']] = $info['requires']; | ||||
$requires_map[$info['hash']] = $info['requires']; | $requires_map[$info['hash']] = $info['requires']; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
$this->detectGraphCycles($resource_graph); | $this->detectGraphCycles($resource_graph); | ||||
$name_map = ipull($binary_map, 'hash') + ipull($text_map, 'hash'); | |||||
$hash_map = array_flip($name_map); | |||||
$hash_map = ipull($binary_map, 'hash') + ipull($text_map, 'hash'); | $package_map = $this->rebuildPackages( | ||||
$resources, | |||||
$symbol_map, | |||||
$hash_map); | |||||
$this->log( | |||||
pht( | |||||
'Found %d packages.', | |||||
new PhutilNumber(count($package_map)))); | |||||
// TODO: Actually do things. | $component_map = array(); | ||||
foreach ($package_map as $package_name => $package_info) { | |||||
foreach ($package_info['symbols'] as $symbol) { | |||||
$component_map[$symbol] = $package_name; | |||||
} | |||||
} | |||||
var_dump($provides_map); | $name_map = $this->mergeNameMaps( | ||||
var_dump($requires_map); | array( | ||||
var_dump($hash_map); | array(pht('Binary'), ipull($binary_map, 'hash')), | ||||
array(pht('Text'), ipull($text_map, 'hash')), | |||||
array(pht('Package'), ipull($package_map, 'hash')), | |||||
)); | |||||
$package_map = ipull($package_map, 'symbols'); | |||||
ksort($name_map); | |||||
ksort($symbol_map); | |||||
ksort($requires_map); | |||||
ksort($package_map); | |||||
$map_content = $this->formatMapContent(array( | |||||
'names' => $name_map, | |||||
'symbols' => $symbol_map, | |||||
'requires' => $requires_map, | |||||
'packages' => $package_map, | |||||
)); | |||||
$map_path = $resources->getPathToMap(); | |||||
$this->log(pht('Writing map "%s".', Filesystem::readablePath($map_path))); | |||||
Filesystem::writeFile($map_path, $map_content); | |||||
} | } | ||||
/** | /** | ||||
* Find binary resources (like PNG and SWF) and return information about | * Find binary resources (like PNG and SWF) and return information about | ||||
* them. | * them. | ||||
* | * | ||||
* @param CelerityResources Resource map to find binary resources for. | * @param CelerityResources Resource map to find binary resources for. | ||||
▲ Show 20 Lines • Show All 123 Lines • ▼ Show 20 Lines | foreach ($nodes as $provides => $requires) { | ||||
throw new Exception( | throw new Exception( | ||||
pht( | pht( | ||||
'Cycle detected in resource graph: %s', | 'Cycle detected in resource graph: %s', | ||||
implode(' > ', $cycle))); | implode(' > ', $cycle))); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
/** | |||||
* Build package specifications for a given resource source. | |||||
* | |||||
* @param CelerityResources Resource source to rebuild. | |||||
* @param list<string, string> Map of `@provides` to hashes. | |||||
* @param list<string, string> Map of hashes to resource names. | |||||
* @return map<string, map<string, string>> Package information maps. | |||||
*/ | |||||
private function rebuildPackages( | |||||
CelerityResources $resources, | |||||
array $symbol_map, | |||||
array $reverse_map) { | |||||
$package_map = array(); | |||||
$package_spec = $resources->getResourcePackages(); | |||||
foreach ($package_spec as $package_name => $package_symbols) { | |||||
$type = null; | |||||
$hashes = array(); | |||||
foreach ($package_symbols as $symbol) { | |||||
$symbol_hash = idx($symbol_map, $symbol); | |||||
if ($symbol_hash === null) { | |||||
throw new Exception( | |||||
pht( | |||||
'Package specification for "%s" includes "%s", but that symbol '. | |||||
'is not @provided by any resource.', | |||||
$package_name, | |||||
$symbol)); | |||||
} | |||||
$resource_name = $reverse_map[$symbol_hash]; | |||||
$resource_type = $resources->getResourceType($resource_name); | |||||
if ($type === null) { | |||||
$type = $resource_type; | |||||
} else if ($type !== $resource_type) { | |||||
throw new Exception( | |||||
pht( | |||||
'Package specification for "%s" includes resources of multiple '. | |||||
'types (%s, %s). Each package may only contain one type of '. | |||||
'resource.', | |||||
$package_name, | |||||
$type, | |||||
$resource_type)); | |||||
} | |||||
$hashes[] = $symbol.':'.$symbol_hash; | |||||
} | |||||
$hash = $resources->getCelerityHash(implode("\n", $hashes)); | |||||
$package_map[$package_name] = array( | |||||
'hash' => $hash, | |||||
'symbols' => $package_symbols, | |||||
); | |||||
} | |||||
return $package_map; | |||||
} | |||||
private function mergeNameMaps(array $maps) { | |||||
$result = array(); | |||||
$origin = array(); | |||||
foreach ($maps as $map) { | |||||
list($map_name, $data) = $map; | |||||
foreach ($data as $name => $hash) { | |||||
if (empty($result[$name])) { | |||||
$result[$name] = $hash; | |||||
$origin[$name] = $map_name; | |||||
} else { | |||||
$old = $origin[$name]; | |||||
$new = $map_name; | |||||
throw new Exception( | |||||
pht( | |||||
'Resource source defines two resources with the same name, '. | |||||
'"%s". One is defined in the "%s" map; the other in the "%s" '. | |||||
'map. Each resource must have a unique name.', | |||||
$name, | |||||
$old, | |||||
$new)); | |||||
} | |||||
} | |||||
} | |||||
return $result; | |||||
} | |||||
private function log($message) { | |||||
$console = PhutilConsole::getConsole(); | |||||
$console->writeErr("%s\n", $message); | |||||
} | |||||
private function formatMapContent(array $data) { | |||||
$content = var_export($data, true); | |||||
$content = preg_replace('/\s+$/m', '', $content); | |||||
$content = preg_replace('/array \(/', 'array(', $content); | |||||
$generated = '@'.'generated'; | |||||
return <<<EOFILE | |||||
<?php | |||||
/** | |||||
* This file is automatically generated. Use 'bin/celerity map' to rebuild it. | |||||
* {$generated} | |||||
*/ | |||||
return {$content}; | |||||
EOFILE; | |||||
} | |||||
} | } |