Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F14003073
D9817.id23558.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
22 KB
Referenced Files
None
Subscribers
None
D9817.id23558.diff
View Options
diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -107,6 +107,7 @@
'CelerityResourceController' => 'infrastructure/celerity/CelerityResourceController.php',
'CelerityResourceGraph' => 'infrastructure/celerity/CelerityResourceGraph.php',
'CelerityResourceMap' => 'infrastructure/celerity/CelerityResourceMap.php',
+ 'CelerityResourceMapGenerator' => 'infrastructure/celerity/CelerityResourceMapGenerator.php',
'CelerityResourceTransformer' => 'infrastructure/celerity/CelerityResourceTransformer.php',
'CelerityResourceTransformerTestCase' => 'infrastructure/celerity/__tests__/CelerityResourceTransformerTestCase.php',
'CelerityResources' => 'infrastructure/celerity/resources/CelerityResources.php',
diff --git a/src/__tests__/PhabricatorInfrastructureTestCase.php b/src/__tests__/PhabricatorInfrastructureTestCase.php
--- a/src/__tests__/PhabricatorInfrastructureTestCase.php
+++ b/src/__tests__/PhabricatorInfrastructureTestCase.php
@@ -10,7 +10,7 @@
}
/**
- * This is more of an acceptance test case instead of a unittest. It verifies
+ * This is more of an acceptance test case instead of a unit test. It verifies
* that all symbols can be loaded correctly. It can catch problems like
* missing methods in descendants of abstract base classes.
*/
@@ -19,6 +19,34 @@
$this->assertTrue(true);
}
+ /**
+ * This is more of an acceptance test case instead of a unit test. It verifies
+ * that the Celerity map is up-to-date.
+ */
+ public function testCelerityMaps() {
+ $resources_map = CelerityPhysicalResources::getAll();
+
+ foreach ($resources_map as $resources) {
+ $old_map = new CelerityResourceMap($resources);
+
+ $new_map = id(new CelerityResourceMapGenerator($resources))
+ ->generate();
+
+ $this->assertEqual(
+ $new_map->getNameMap(),
+ $old_map->getNameMap());
+ $this->assertEqual(
+ $new_map->getSymbolMap(),
+ $old_map->getSymbolMap());
+ $this->assertEqual(
+ $new_map->getRequiresMap(),
+ $old_map->getRequiresMap());
+ $this->assertEqual(
+ $new_map->getPackageMap(),
+ $old_map->getPackageMap());
+ }
+ }
+
public function testApplicationsInstalled() {
$all = PhabricatorApplication::getAllApplications();
$installed = PhabricatorApplication::getAllInstalledApplications();
diff --git a/src/infrastructure/celerity/CelerityResourceMap.php b/src/infrastructure/celerity/CelerityResourceMap.php
--- a/src/infrastructure/celerity/CelerityResourceMap.php
+++ b/src/infrastructure/celerity/CelerityResourceMap.php
@@ -53,6 +53,22 @@
return self::$instances[$name];
}
+ public function getNameMap() {
+ return $this->nameMap;
+ }
+
+ public function getSymbolMap() {
+ return $this->symbolMap;
+ }
+
+ public function getRequiresMap() {
+ return $this->requiresMap;
+ }
+
+ public function getPackageMap() {
+ return $this->packageMap;
+ }
+
public function getPackagedNamesForSymbols(array $symbols) {
$resolved = $this->resolveResources($symbols);
return $this->packageResources($resolved);
diff --git a/src/infrastructure/celerity/management/CelerityManagementMapWorkflow.php b/src/infrastructure/celerity/CelerityResourceMapGenerator.php
copy from src/infrastructure/celerity/management/CelerityManagementMapWorkflow.php
copy to src/infrastructure/celerity/CelerityResourceMapGenerator.php
--- a/src/infrastructure/celerity/management/CelerityManagementMapWorkflow.php
+++ b/src/infrastructure/celerity/CelerityResourceMapGenerator.php
@@ -1,64 +1,59 @@
<?php
-final class CelerityManagementMapWorkflow
- extends CelerityManagementWorkflow {
-
- public function didConstruct() {
- $this
- ->setName('map')
- ->setExamples('**map** [options]')
- ->setSynopsis(pht('Rebuild static resource maps.'))
- ->setArguments(
- array());
+final class CelerityResourceMapGenerator {
+
+ private $debug = false;
+ private $resources;
+
+ private $nameMap = array();
+ private $symbolMap = array();
+ private $requiresMap = array();
+ private $packageMap = array();
+
+ public function __construct(CelerityPhysicalResources $resources) {
+ $this->resources = $resources;
}
- public function execute(PhutilArgumentParser $args) {
- $resources_map = CelerityPhysicalResources::getAll();
+ public function getNameMap() {
+ return $this->nameMap;
+ }
- $this->log(
- pht(
- 'Rebuilding %d resource source(s).',
- new PhutilNumber(count($resources_map))));
+ public function getSymbolMap() {
+ return $this->symbolMap;
+ }
- foreach ($resources_map as $name => $resources) {
- $this->rebuildResources($resources);
- }
+ public function getRequiresMap() {
+ return $this->requiresMap;
+ }
- $this->log(pht('Done.'));
+ public function getPackageMap() {
+ return $this->packageMap;
+ }
- return 0;
+ public function setDebug($debug) {
+ $this->debug = $debug;
+ return $this;
}
- /**
- * Rebuild the resource map for a resource source.
- *
- * @param CelerityPhysicalResources Resource source to rebuild.
- * @return void
- */
- private function rebuildResources(CelerityPhysicalResources $resources) {
- $this->log(
- pht(
- 'Rebuilding resource source "%s" (%s)...',
- $resources->getName(),
- get_class($resources)));
+ protected function log($message) {
+ if ($this->debug) {
+ $console = PhutilConsole::getConsole();
+ $console->writeErr("%s\n", $message);
+ }
+ }
- $binary_map = $this->rebuildBinaryResources($resources);
+ public function generate() {
+ $binary_map = $this->rebuildBinaryResources($this->resources);
- $this->log(
- pht(
- 'Found %d binary resources.',
- new PhutilNumber(count($binary_map))));
+ $this->log(pht('Found %d binary resources.', count($binary_map)));
$xformer = id(new CelerityResourceTransformer())
->setMinify(false)
->setRawURIMap(ipull($binary_map, 'uri'));
- $text_map = $this->rebuildTextResources($resources, $xformer);
+ $text_map = $this->rebuildTextResources($this->resources, $xformer);
- $this->log(
- pht(
- 'Found %d text resources.',
- new PhutilNumber(count($text_map))));
+ $this->log(pht('Found %d text resources.', count($text_map)));
$resource_graph = array();
$requires_map = array();
@@ -81,14 +76,11 @@
$hash_map = array_flip($name_map);
$package_map = $this->rebuildPackages(
- $resources,
+ $this->resources,
$symbol_map,
$hash_map);
- $this->log(
- pht(
- 'Found %d packages.',
- new PhutilNumber(count($package_map))));
+ $this->log(pht('Found %d packages.', count($package_map)));
$component_map = array();
foreach ($package_map as $package_name => $package_info) {
@@ -110,18 +102,46 @@
ksort($requires_map);
ksort($package_map);
+ $this->nameMap = $name_map;
+ $this->symbolMap = $symbol_map;
+ $this->requiresMap = $requires_map;
+ $this->packageMap = $package_map;
+
+ return $this;
+ }
+
+ public function write() {
$map_content = $this->formatMapContent(array(
- 'names' => $name_map,
- 'symbols' => $symbol_map,
- 'requires' => $requires_map,
- 'packages' => $package_map,
+ 'names' => $this->getNameMap(),
+ 'symbols' => $this->getSymbolMap(),
+ 'requires' => $this->getRequiresMap(),
+ 'packages' => $this->getPackageMap(),
));
- $map_path = $resources->getPathToMap();
+ $map_path = $this->resources->getPathToMap();
$this->log(pht('Writing map "%s".', Filesystem::readablePath($map_path)));
Filesystem::writeFile($map_path, $map_content);
+
+ return $this;
}
+ 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;
+ }
/**
* Find binary resources (like PNG and SWF) and return information about
@@ -134,21 +154,20 @@
CelerityPhysicalResources $resources) {
$binary_map = $resources->findBinaryResources();
-
$result_map = array();
+
foreach ($binary_map as $name => $data_hash) {
$hash = $resources->getCelerityHash($data_hash.$name);
$result_map[$name] = array(
'hash' => $hash,
- 'uri' => $resources->getResourceURI($hash, $name),
+ 'uri' => $resources->getResourceURI($hash, $name),
);
}
return $result_map;
}
-
/**
* Find text resources (like JS and CSS) and return information about them.
*
@@ -161,8 +180,8 @@
CelerityResourceTransformer $xformer) {
$text_map = $resources->findTextResources();
-
$result_map = array();
+
foreach ($text_map as $name => $data_hash) {
$raw_data = $resources->getResourceData($name);
$xformed_data = $xformer->transformResource($name, $raw_data);
@@ -189,15 +208,14 @@
return $result_map;
}
-
/**
* Parse the `@provides` and `@requires` symbols out of a text resource, like
* JS or CSS.
*
* @param string Resource name.
* @param string Resource data.
- * @return pair<string|null, list<string>|null> The `@provides` symbol and the
- * list of `@requires` symbols. If the resource is not part of the
+ * @return pair<string|null, list<string>|null> The `@provides` symbol and
+ * the list of `@requires` symbols. If the resource is not part of the
* dependency graph, both are null.
*/
private function getProvidesAndRequires($name, $data) {
@@ -227,15 +245,12 @@
if (count($provides) > 1) {
throw new Exception(
- pht(
- 'Resource "%s" must @provide at most one Celerity target.',
- $name));
+ pht('Resource "%s" must @provide at most one Celerity target.', $name));
}
return array(head($provides), $requires);
}
-
/**
* Check for dependency cycles in the resource graph. Raises an exception if
* a cycle is detected.
@@ -254,9 +269,7 @@
$cycle = $graph->detectCycles($provides);
if ($cycle) {
throw new Exception(
- pht(
- 'Cycle detected in resource graph: %s',
- implode(' > ', $cycle)));
+ pht('Cycle detected in resource graph: %s', implode(' > ', $cycle)));
}
}
}
@@ -265,8 +278,8 @@
* Build package specifications for a given resource source.
*
* @param CelerityPhysicalResources Resource source to rebuild.
- * @param list<string, string> Map of `@provides` to hashes.
- * @param list<string, string> Map of hashes to resource names.
+ * @param map<string, string> Map of `@provides` to hashes.
+ * @param map<string, string> Map of hashes to resource names.
* @return map<string, map<string, string>> Package information maps.
*/
private function rebuildPackages(
@@ -322,6 +335,7 @@
private function mergeNameMaps(array $maps) {
$result = array();
$origin = array();
+
foreach ($maps as $map) {
list($map_name, $data) = $map;
foreach ($data as $name => $hash) {
@@ -345,28 +359,4 @@
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;
- }
-
-
}
diff --git a/src/infrastructure/celerity/management/CelerityManagementMapWorkflow.php b/src/infrastructure/celerity/management/CelerityManagementMapWorkflow.php
--- a/src/infrastructure/celerity/management/CelerityManagementMapWorkflow.php
+++ b/src/infrastructure/celerity/management/CelerityManagementMapWorkflow.php
@@ -42,331 +42,15 @@
$resources->getName(),
get_class($resources)));
- $binary_map = $this->rebuildBinaryResources($resources);
-
- $this->log(
- pht(
- 'Found %d binary resources.',
- new PhutilNumber(count($binary_map))));
-
- $xformer = id(new CelerityResourceTransformer())
- ->setMinify(false)
- ->setRawURIMap(ipull($binary_map, 'uri'));
-
- $text_map = $this->rebuildTextResources($resources, $xformer);
-
- $this->log(
- pht(
- 'Found %d text resources.',
- new PhutilNumber(count($text_map))));
-
- $resource_graph = array();
- $requires_map = array();
- $symbol_map = array();
- foreach ($text_map as $name => $info) {
- if (isset($info['provides'])) {
- $symbol_map[$info['provides']] = $info['hash'];
-
- // We only need to check for cycles and add this to the requires map
- // if it actually requires anything.
- if (!empty($info['requires'])) {
- $resource_graph[$info['provides']] = $info['requires'];
- $requires_map[$info['hash']] = $info['requires'];
- }
- }
- }
-
- $this->detectGraphCycles($resource_graph);
- $name_map = ipull($binary_map, 'hash') + ipull($text_map, 'hash');
- $hash_map = array_flip($name_map);
-
- $package_map = $this->rebuildPackages(
- $resources,
- $symbol_map,
- $hash_map);
-
- $this->log(
- pht(
- 'Found %d packages.',
- new PhutilNumber(count($package_map))));
-
- $component_map = array();
- foreach ($package_map as $package_name => $package_info) {
- foreach ($package_info['symbols'] as $symbol) {
- $component_map[$symbol] = $package_name;
- }
- }
-
- $name_map = $this->mergeNameMaps(
- array(
- 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
- * them.
- *
- * @param CelerityPhysicalResources Resource map to find binary resources for.
- * @return map<string, map<string, string>> Resource information map.
- */
- private function rebuildBinaryResources(
- CelerityPhysicalResources $resources) {
-
- $binary_map = $resources->findBinaryResources();
-
- $result_map = array();
- foreach ($binary_map as $name => $data_hash) {
- $hash = $resources->getCelerityHash($data_hash.$name);
-
- $result_map[$name] = array(
- 'hash' => $hash,
- 'uri' => $resources->getResourceURI($hash, $name),
- );
- }
-
- return $result_map;
- }
-
-
- /**
- * Find text resources (like JS and CSS) and return information about them.
- *
- * @param CelerityPhysicalResources Resource map to find text resources for.
- * @param CelerityResourceTransformer Configured resource transformer.
- * @return map<string, map<string, string>> Resource information map.
- */
- private function rebuildTextResources(
- CelerityPhysicalResources $resources,
- CelerityResourceTransformer $xformer) {
-
- $text_map = $resources->findTextResources();
-
- $result_map = array();
- foreach ($text_map as $name => $data_hash) {
- $raw_data = $resources->getResourceData($name);
- $xformed_data = $xformer->transformResource($name, $raw_data);
-
- $data_hash = $resources->getCelerityHash($xformed_data);
- $hash = $resources->getCelerityHash($data_hash.$name);
-
- list($provides, $requires) = $this->getProvidesAndRequires(
- $name,
- $raw_data);
-
- $result_map[$name] = array(
- 'hash' => $hash,
- );
-
- if ($provides !== null) {
- $result_map[$name] += array(
- 'provides' => $provides,
- 'requires' => $requires,
- );
- }
- }
-
- return $result_map;
+ id(new CelerityResourceMapGenerator($resources))
+ ->setDebug(true)
+ ->generate()
+ ->write();
}
-
- /**
- * Parse the `@provides` and `@requires` symbols out of a text resource, like
- * JS or CSS.
- *
- * @param string Resource name.
- * @param string Resource data.
- * @return pair<string|null, list<string>|null> The `@provides` symbol and the
- * list of `@requires` symbols. If the resource is not part of the
- * dependency graph, both are null.
- */
- private function getProvidesAndRequires($name, $data) {
- $parser = new PhutilDocblockParser();
-
- $matches = array();
- $ok = preg_match('@/[*][*].*?[*]/@s', $data, $matches);
- if (!$ok) {
- throw new Exception(
- pht(
- 'Resource "%s" does not have a header doc comment. Encode '.
- 'dependency data in a header docblock.',
- $name));
- }
-
- list($description, $metadata) = $parser->parse($matches[0]);
-
- $provides = preg_split('/\s+/', trim(idx($metadata, 'provides')));
- $requires = preg_split('/\s+/', trim(idx($metadata, 'requires')));
- $provides = array_filter($provides);
- $requires = array_filter($requires);
-
- if (!$provides) {
- // Tests and documentation-only JS is permitted to @provide no targets.
- return array(null, null);
- }
-
- if (count($provides) > 1) {
- throw new Exception(
- pht(
- 'Resource "%s" must @provide at most one Celerity target.',
- $name));
- }
-
- return array(head($provides), $requires);
- }
-
-
- /**
- * Check for dependency cycles in the resource graph. Raises an exception if
- * a cycle is detected.
- *
- * @param map<string, list<string>> Map of `@provides` symbols to their
- * `@requires` symbols.
- * @return void
- */
- private function detectGraphCycles(array $nodes) {
- $graph = id(new CelerityResourceGraph())
- ->addNodes($nodes)
- ->setResourceGraph($nodes)
- ->loadGraph();
-
- foreach ($nodes as $provides => $requires) {
- $cycle = $graph->detectCycles($provides);
- if ($cycle) {
- throw new Exception(
- pht(
- 'Cycle detected in resource graph: %s',
- implode(' > ', $cycle)));
- }
- }
- }
-
- /**
- * Build package specifications for a given resource source.
- *
- * @param CelerityPhysicalResources 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(
- CelerityPhysicalResources $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) {
+ protected 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;
- }
-
-
}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sun, Oct 27, 1:53 AM (2 w, 3 d ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
6751736
Default Alt Text
D9817.id23558.diff (22 KB)
Attached To
Mode
D9817: Add an acceptance test for Celerity maps
Attached
Detach File
Event Timeline
Log In to Comment