@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Add an acceptance test for Celerity maps

Summary: Fixes T5374. Add an acceptance test to the `PhabricatorInfrastructureTestCase` class which fails if a Celerity map is not up-to-date. In order to achieve this, a lot of code used to generate Celerity maps was transferred from `CelerityManagementMapWorkflow` to `CelerityResourceMap` and `CelerityResourceMapGenerator`.

Test Plan: Ran `arc unit` and noticed that all tests passed. Modified a JavaScript file and ran `arc unit` again (without running `./bin/celerity map`)... this time the test failed, as expected.

Reviewers: epriestley, #blessed_reviewers

Reviewed By: epriestley, #blessed_reviewers

Subscribers: epriestley, Korvin

Maniphest Tasks: T5374

Differential Revision: https://secure.phabricator.com/D9817

+418 -320
+3
src/__phutil_library_map__.php
··· 111 111 'CelerityResourceController' => 'infrastructure/celerity/CelerityResourceController.php', 112 112 'CelerityResourceGraph' => 'infrastructure/celerity/CelerityResourceGraph.php', 113 113 'CelerityResourceMap' => 'infrastructure/celerity/CelerityResourceMap.php', 114 + 'CelerityResourceMapGenerator' => 'infrastructure/celerity/CelerityResourceMapGenerator.php', 114 115 'CelerityResourceTransformer' => 'infrastructure/celerity/CelerityResourceTransformer.php', 115 116 'CelerityResourceTransformerTestCase' => 'infrastructure/celerity/__tests__/CelerityResourceTransformerTestCase.php', 116 117 'CelerityResources' => 'infrastructure/celerity/resources/CelerityResources.php', ··· 1304 1305 'PhabricatorCalendarSchemaSpec' => 'applications/calendar/storage/PhabricatorCalendarSchemaSpec.php', 1305 1306 'PhabricatorCalendarViewController' => 'applications/calendar/controller/PhabricatorCalendarViewController.php', 1306 1307 'PhabricatorCampfireProtocolAdapter' => 'infrastructure/daemon/bot/adapter/PhabricatorCampfireProtocolAdapter.php', 1308 + 'PhabricatorCelerityTestCase' => '__tests__/PhabricatorCelerityTestCase.php', 1307 1309 'PhabricatorChangeParserTestCase' => 'applications/repository/worker/__tests__/PhabricatorChangeParserTestCase.php', 1308 1310 'PhabricatorChangesetResponse' => 'infrastructure/diff/PhabricatorChangesetResponse.php', 1309 1311 'PhabricatorChatLogApplication' => 'applications/chatlog/application/PhabricatorChatLogApplication.php', ··· 4216 4218 'PhabricatorCalendarSchemaSpec' => 'PhabricatorConfigSchemaSpec', 4217 4219 'PhabricatorCalendarViewController' => 'PhabricatorCalendarController', 4218 4220 'PhabricatorCampfireProtocolAdapter' => 'PhabricatorBotBaseStreamingProtocolAdapter', 4221 + 'PhabricatorCelerityTestCase' => 'PhabricatorTestCase', 4219 4222 'PhabricatorChangeParserTestCase' => 'PhabricatorWorkingCopyTestCase', 4220 4223 'PhabricatorChangesetResponse' => 'AphrontProxyResponse', 4221 4224 'PhabricatorChatLogApplication' => 'PhabricatorApplication',
+33
src/__tests__/PhabricatorCelerityTestCase.php
··· 1 + <?php 2 + 3 + final class PhabricatorCelerityTestCase extends PhabricatorTestCase { 4 + 5 + /** 6 + * This is more of an acceptance test case instead of a unit test. It verifies 7 + * that the Celerity map is up-to-date. 8 + */ 9 + public function testCelerityMaps() { 10 + $resources_map = CelerityPhysicalResources::getAll(); 11 + 12 + foreach ($resources_map as $resources) { 13 + $old_map = new CelerityResourceMap($resources); 14 + 15 + $new_map = id(new CelerityResourceMapGenerator($resources)) 16 + ->generate(); 17 + 18 + $this->assertEqual( 19 + $new_map->getNameMap(), 20 + $old_map->getNameMap()); 21 + $this->assertEqual( 22 + $new_map->getSymbolMap(), 23 + $old_map->getSymbolMap()); 24 + $this->assertEqual( 25 + $new_map->getRequiresMap(), 26 + $old_map->getRequiresMap()); 27 + $this->assertEqual( 28 + $new_map->getPackageMap(), 29 + $old_map->getPackageMap()); 30 + } 31 + } 32 + 33 + }
+16
src/infrastructure/celerity/CelerityResourceMap.php
··· 53 53 return self::$instances[$name]; 54 54 } 55 55 56 + public function getNameMap() { 57 + return $this->nameMap; 58 + } 59 + 60 + public function getSymbolMap() { 61 + return $this->symbolMap; 62 + } 63 + 64 + public function getRequiresMap() { 65 + return $this->requiresMap; 66 + } 67 + 68 + public function getPackageMap() { 69 + return $this->packageMap; 70 + } 71 + 56 72 public function getPackagedNamesForSymbols(array $symbols) { 57 73 $resolved = $this->resolveResources($symbols); 58 74 return $this->packageResources($resolved);
+361
src/infrastructure/celerity/CelerityResourceMapGenerator.php
··· 1 + <?php 2 + 3 + final class CelerityResourceMapGenerator { 4 + 5 + private $debug = false; 6 + private $resources; 7 + 8 + private $nameMap = array(); 9 + private $symbolMap = array(); 10 + private $requiresMap = array(); 11 + private $packageMap = array(); 12 + 13 + public function __construct(CelerityPhysicalResources $resources) { 14 + $this->resources = $resources; 15 + } 16 + 17 + public function getNameMap() { 18 + return $this->nameMap; 19 + } 20 + 21 + public function getSymbolMap() { 22 + return $this->symbolMap; 23 + } 24 + 25 + public function getRequiresMap() { 26 + return $this->requiresMap; 27 + } 28 + 29 + public function getPackageMap() { 30 + return $this->packageMap; 31 + } 32 + 33 + public function setDebug($debug) { 34 + $this->debug = $debug; 35 + return $this; 36 + } 37 + 38 + protected function log($message) { 39 + if ($this->debug) { 40 + $console = PhutilConsole::getConsole(); 41 + $console->writeErr("%s\n", $message); 42 + } 43 + } 44 + 45 + public function generate() { 46 + $binary_map = $this->rebuildBinaryResources($this->resources); 47 + 48 + $this->log(pht('Found %d binary resources.', count($binary_map))); 49 + 50 + $xformer = id(new CelerityResourceTransformer()) 51 + ->setMinify(false) 52 + ->setRawURIMap(ipull($binary_map, 'uri')); 53 + 54 + $text_map = $this->rebuildTextResources($this->resources, $xformer); 55 + 56 + $this->log(pht('Found %d text resources.', count($text_map))); 57 + 58 + $resource_graph = array(); 59 + $requires_map = array(); 60 + $symbol_map = array(); 61 + foreach ($text_map as $name => $info) { 62 + if (isset($info['provides'])) { 63 + $symbol_map[$info['provides']] = $info['hash']; 64 + 65 + // We only need to check for cycles and add this to the requires map 66 + // if it actually requires anything. 67 + if (!empty($info['requires'])) { 68 + $resource_graph[$info['provides']] = $info['requires']; 69 + $requires_map[$info['hash']] = $info['requires']; 70 + } 71 + } 72 + } 73 + 74 + $this->detectGraphCycles($resource_graph); 75 + $name_map = ipull($binary_map, 'hash') + ipull($text_map, 'hash'); 76 + $hash_map = array_flip($name_map); 77 + 78 + $package_map = $this->rebuildPackages( 79 + $this->resources, 80 + $symbol_map, 81 + $hash_map); 82 + 83 + $this->log(pht('Found %d packages.', count($package_map))); 84 + 85 + $component_map = array(); 86 + foreach ($package_map as $package_name => $package_info) { 87 + foreach ($package_info['symbols'] as $symbol) { 88 + $component_map[$symbol] = $package_name; 89 + } 90 + } 91 + 92 + $name_map = $this->mergeNameMaps( 93 + array( 94 + array(pht('Binary'), ipull($binary_map, 'hash')), 95 + array(pht('Text'), ipull($text_map, 'hash')), 96 + array(pht('Package'), ipull($package_map, 'hash')), 97 + )); 98 + $package_map = ipull($package_map, 'symbols'); 99 + 100 + ksort($name_map); 101 + ksort($symbol_map); 102 + ksort($requires_map); 103 + ksort($package_map); 104 + 105 + $this->nameMap = $name_map; 106 + $this->symbolMap = $symbol_map; 107 + $this->requiresMap = $requires_map; 108 + $this->packageMap = $package_map; 109 + 110 + return $this; 111 + } 112 + 113 + public function write() { 114 + $map_content = $this->formatMapContent(array( 115 + 'names' => $this->getNameMap(), 116 + 'symbols' => $this->getSymbolMap(), 117 + 'requires' => $this->getRequiresMap(), 118 + 'packages' => $this->getPackageMap(), 119 + )); 120 + 121 + $map_path = $this->resources->getPathToMap(); 122 + $this->log(pht('Writing map "%s".', Filesystem::readablePath($map_path))); 123 + Filesystem::writeFile($map_path, $map_content); 124 + 125 + return $this; 126 + } 127 + 128 + private function formatMapContent(array $data) { 129 + $content = phutil_var_export($data); 130 + $generated = '@'.'generated'; 131 + 132 + return <<<EOFILE 133 + <?php 134 + 135 + /** 136 + * This file is automatically generated. Use 'bin/celerity map' to rebuild it. 137 + * 138 + * {$generated} 139 + */ 140 + return {$content}; 141 + 142 + EOFILE; 143 + } 144 + 145 + /** 146 + * Find binary resources (like PNG and SWF) and return information about 147 + * them. 148 + * 149 + * @param CelerityPhysicalResources Resource map to find binary resources for. 150 + * @return map<string, map<string, string>> Resource information map. 151 + */ 152 + private function rebuildBinaryResources( 153 + CelerityPhysicalResources $resources) { 154 + 155 + $binary_map = $resources->findBinaryResources(); 156 + $result_map = array(); 157 + 158 + foreach ($binary_map as $name => $data_hash) { 159 + $hash = $resources->getCelerityHash($data_hash.$name); 160 + 161 + $result_map[$name] = array( 162 + 'hash' => $hash, 163 + 'uri' => $resources->getResourceURI($hash, $name), 164 + ); 165 + } 166 + 167 + return $result_map; 168 + } 169 + 170 + /** 171 + * Find text resources (like JS and CSS) and return information about them. 172 + * 173 + * @param CelerityPhysicalResources Resource map to find text resources for. 174 + * @param CelerityResourceTransformer Configured resource transformer. 175 + * @return map<string, map<string, string>> Resource information map. 176 + */ 177 + private function rebuildTextResources( 178 + CelerityPhysicalResources $resources, 179 + CelerityResourceTransformer $xformer) { 180 + 181 + $text_map = $resources->findTextResources(); 182 + $result_map = array(); 183 + 184 + foreach ($text_map as $name => $data_hash) { 185 + $raw_data = $resources->getResourceData($name); 186 + $xformed_data = $xformer->transformResource($name, $raw_data); 187 + 188 + $data_hash = $resources->getCelerityHash($xformed_data); 189 + $hash = $resources->getCelerityHash($data_hash.$name); 190 + 191 + list($provides, $requires) = $this->getProvidesAndRequires( 192 + $name, 193 + $raw_data); 194 + 195 + $result_map[$name] = array( 196 + 'hash' => $hash, 197 + ); 198 + 199 + if ($provides !== null) { 200 + $result_map[$name] += array( 201 + 'provides' => $provides, 202 + 'requires' => $requires, 203 + ); 204 + } 205 + } 206 + 207 + return $result_map; 208 + } 209 + 210 + /** 211 + * Parse the `@provides` and `@requires` symbols out of a text resource, like 212 + * JS or CSS. 213 + * 214 + * @param string Resource name. 215 + * @param string Resource data. 216 + * @return pair<string|null, list<string>|null> The `@provides` symbol and 217 + * the list of `@requires` symbols. If the resource is not part of the 218 + * dependency graph, both are null. 219 + */ 220 + private function getProvidesAndRequires($name, $data) { 221 + $parser = new PhutilDocblockParser(); 222 + 223 + $matches = array(); 224 + $ok = preg_match('@/[*][*].*?[*]/@s', $data, $matches); 225 + if (!$ok) { 226 + throw new Exception( 227 + pht( 228 + 'Resource "%s" does not have a header doc comment. Encode '. 229 + 'dependency data in a header docblock.', 230 + $name)); 231 + } 232 + 233 + list($description, $metadata) = $parser->parse($matches[0]); 234 + 235 + $provides = preg_split('/\s+/', trim(idx($metadata, 'provides'))); 236 + $requires = preg_split('/\s+/', trim(idx($metadata, 'requires'))); 237 + $provides = array_filter($provides); 238 + $requires = array_filter($requires); 239 + 240 + if (!$provides) { 241 + // Tests and documentation-only JS is permitted to @provide no targets. 242 + return array(null, null); 243 + } 244 + 245 + if (count($provides) > 1) { 246 + throw new Exception( 247 + pht('Resource "%s" must @provide at most one Celerity target.', $name)); 248 + } 249 + 250 + return array(head($provides), $requires); 251 + } 252 + 253 + /** 254 + * Check for dependency cycles in the resource graph. Raises an exception if 255 + * a cycle is detected. 256 + * 257 + * @param map<string, list<string>> Map of `@provides` symbols to their 258 + * `@requires` symbols. 259 + * @return void 260 + */ 261 + private function detectGraphCycles(array $nodes) { 262 + $graph = id(new CelerityResourceGraph()) 263 + ->addNodes($nodes) 264 + ->setResourceGraph($nodes) 265 + ->loadGraph(); 266 + 267 + foreach ($nodes as $provides => $requires) { 268 + $cycle = $graph->detectCycles($provides); 269 + if ($cycle) { 270 + throw new Exception( 271 + pht('Cycle detected in resource graph: %s', implode(' > ', $cycle))); 272 + } 273 + } 274 + } 275 + 276 + /** 277 + * Build package specifications for a given resource source. 278 + * 279 + * @param CelerityPhysicalResources Resource source to rebuild. 280 + * @param map<string, string> Map of `@provides` to hashes. 281 + * @param map<string, string> Map of hashes to resource names. 282 + * @return map<string, map<string, string>> Package information maps. 283 + */ 284 + private function rebuildPackages( 285 + CelerityPhysicalResources $resources, 286 + array $symbol_map, 287 + array $reverse_map) { 288 + 289 + $package_map = array(); 290 + 291 + $package_spec = $resources->getResourcePackages(); 292 + foreach ($package_spec as $package_name => $package_symbols) { 293 + $type = null; 294 + $hashes = array(); 295 + foreach ($package_symbols as $symbol) { 296 + $symbol_hash = idx($symbol_map, $symbol); 297 + if ($symbol_hash === null) { 298 + throw new Exception( 299 + pht( 300 + 'Package specification for "%s" includes "%s", but that symbol '. 301 + 'is not @provided by any resource.', 302 + $package_name, 303 + $symbol)); 304 + } 305 + 306 + $resource_name = $reverse_map[$symbol_hash]; 307 + $resource_type = $resources->getResourceType($resource_name); 308 + if ($type === null) { 309 + $type = $resource_type; 310 + } else if ($type !== $resource_type) { 311 + throw new Exception( 312 + pht( 313 + 'Package specification for "%s" includes resources of multiple '. 314 + 'types (%s, %s). Each package may only contain one type of '. 315 + 'resource.', 316 + $package_name, 317 + $type, 318 + $resource_type)); 319 + } 320 + 321 + $hashes[] = $symbol.':'.$symbol_hash; 322 + } 323 + 324 + $hash = $resources->getCelerityHash(implode("\n", $hashes)); 325 + $package_map[$package_name] = array( 326 + 'hash' => $hash, 327 + 'symbols' => $package_symbols, 328 + ); 329 + } 330 + 331 + return $package_map; 332 + } 333 + 334 + private function mergeNameMaps(array $maps) { 335 + $result = array(); 336 + $origin = array(); 337 + 338 + foreach ($maps as $map) { 339 + list($map_name, $data) = $map; 340 + foreach ($data as $name => $hash) { 341 + if (empty($result[$name])) { 342 + $result[$name] = $hash; 343 + $origin[$name] = $map_name; 344 + } else { 345 + $old = $origin[$name]; 346 + $new = $map_name; 347 + throw new Exception( 348 + pht( 349 + 'Resource source defines two resources with the same name, '. 350 + '"%s". One is defined in the "%s" map; the other in the "%s" '. 351 + 'map. Each resource must have a unique name.', 352 + $name, 353 + $old, 354 + $new)); 355 + } 356 + } 357 + } 358 + return $result; 359 + } 360 + 361 + }
+5 -320
src/infrastructure/celerity/management/CelerityManagementMapWorkflow.php
··· 42 42 $resources->getName(), 43 43 get_class($resources))); 44 44 45 - $binary_map = $this->rebuildBinaryResources($resources); 46 - 47 - $this->log( 48 - pht( 49 - 'Found %d binary resources.', 50 - new PhutilNumber(count($binary_map)))); 51 - 52 - $xformer = id(new CelerityResourceTransformer()) 53 - ->setMinify(false) 54 - ->setRawURIMap(ipull($binary_map, 'uri')); 55 - 56 - $text_map = $this->rebuildTextResources($resources, $xformer); 57 - 58 - $this->log( 59 - pht( 60 - 'Found %d text resources.', 61 - new PhutilNumber(count($text_map)))); 62 - 63 - $resource_graph = array(); 64 - $requires_map = array(); 65 - $symbol_map = array(); 66 - foreach ($text_map as $name => $info) { 67 - if (isset($info['provides'])) { 68 - $symbol_map[$info['provides']] = $info['hash']; 69 - 70 - // We only need to check for cycles and add this to the requires map 71 - // if it actually requires anything. 72 - if (!empty($info['requires'])) { 73 - $resource_graph[$info['provides']] = $info['requires']; 74 - $requires_map[$info['hash']] = $info['requires']; 75 - } 76 - } 77 - } 78 - 79 - $this->detectGraphCycles($resource_graph); 80 - $name_map = ipull($binary_map, 'hash') + ipull($text_map, 'hash'); 81 - $hash_map = array_flip($name_map); 82 - 83 - $package_map = $this->rebuildPackages( 84 - $resources, 85 - $symbol_map, 86 - $hash_map); 87 - 88 - $this->log( 89 - pht( 90 - 'Found %d packages.', 91 - new PhutilNumber(count($package_map)))); 92 - 93 - $component_map = array(); 94 - foreach ($package_map as $package_name => $package_info) { 95 - foreach ($package_info['symbols'] as $symbol) { 96 - $component_map[$symbol] = $package_name; 97 - } 98 - } 99 - 100 - $name_map = $this->mergeNameMaps( 101 - array( 102 - array(pht('Binary'), ipull($binary_map, 'hash')), 103 - array(pht('Text'), ipull($text_map, 'hash')), 104 - array(pht('Package'), ipull($package_map, 'hash')), 105 - )); 106 - $package_map = ipull($package_map, 'symbols'); 107 - 108 - ksort($name_map); 109 - ksort($symbol_map); 110 - ksort($requires_map); 111 - ksort($package_map); 112 - 113 - $map_content = $this->formatMapContent(array( 114 - 'names' => $name_map, 115 - 'symbols' => $symbol_map, 116 - 'requires' => $requires_map, 117 - 'packages' => $package_map, 118 - )); 119 - 120 - $map_path = $resources->getPathToMap(); 121 - $this->log(pht('Writing map "%s".', Filesystem::readablePath($map_path))); 122 - Filesystem::writeFile($map_path, $map_content); 45 + id(new CelerityResourceMapGenerator($resources)) 46 + ->setDebug(true) 47 + ->generate() 48 + ->write(); 123 49 } 124 50 125 - 126 - /** 127 - * Find binary resources (like PNG and SWF) and return information about 128 - * them. 129 - * 130 - * @param CelerityPhysicalResources Resource map to find binary resources for. 131 - * @return map<string, map<string, string>> Resource information map. 132 - */ 133 - private function rebuildBinaryResources( 134 - CelerityPhysicalResources $resources) { 135 - 136 - $binary_map = $resources->findBinaryResources(); 137 - 138 - $result_map = array(); 139 - foreach ($binary_map as $name => $data_hash) { 140 - $hash = $resources->getCelerityHash($data_hash.$name); 141 - 142 - $result_map[$name] = array( 143 - 'hash' => $hash, 144 - 'uri' => $resources->getResourceURI($hash, $name), 145 - ); 146 - } 147 - 148 - return $result_map; 149 - } 150 - 151 - 152 - /** 153 - * Find text resources (like JS and CSS) and return information about them. 154 - * 155 - * @param CelerityPhysicalResources Resource map to find text resources for. 156 - * @param CelerityResourceTransformer Configured resource transformer. 157 - * @return map<string, map<string, string>> Resource information map. 158 - */ 159 - private function rebuildTextResources( 160 - CelerityPhysicalResources $resources, 161 - CelerityResourceTransformer $xformer) { 162 - 163 - $text_map = $resources->findTextResources(); 164 - 165 - $result_map = array(); 166 - foreach ($text_map as $name => $data_hash) { 167 - $raw_data = $resources->getResourceData($name); 168 - $xformed_data = $xformer->transformResource($name, $raw_data); 169 - 170 - $data_hash = $resources->getCelerityHash($xformed_data); 171 - $hash = $resources->getCelerityHash($data_hash.$name); 172 - 173 - list($provides, $requires) = $this->getProvidesAndRequires( 174 - $name, 175 - $raw_data); 176 - 177 - $result_map[$name] = array( 178 - 'hash' => $hash, 179 - ); 180 - 181 - if ($provides !== null) { 182 - $result_map[$name] += array( 183 - 'provides' => $provides, 184 - 'requires' => $requires, 185 - ); 186 - } 187 - } 188 - 189 - return $result_map; 190 - } 191 - 192 - 193 - /** 194 - * Parse the `@provides` and `@requires` symbols out of a text resource, like 195 - * JS or CSS. 196 - * 197 - * @param string Resource name. 198 - * @param string Resource data. 199 - * @return pair<string|null, list<string>|null> The `@provides` symbol and the 200 - * list of `@requires` symbols. If the resource is not part of the 201 - * dependency graph, both are null. 202 - */ 203 - private function getProvidesAndRequires($name, $data) { 204 - $parser = new PhutilDocblockParser(); 205 - 206 - $matches = array(); 207 - $ok = preg_match('@/[*][*].*?[*]/@s', $data, $matches); 208 - if (!$ok) { 209 - throw new Exception( 210 - pht( 211 - 'Resource "%s" does not have a header doc comment. Encode '. 212 - 'dependency data in a header docblock.', 213 - $name)); 214 - } 215 - 216 - list($description, $metadata) = $parser->parse($matches[0]); 217 - 218 - $provides = preg_split('/\s+/', trim(idx($metadata, 'provides'))); 219 - $requires = preg_split('/\s+/', trim(idx($metadata, 'requires'))); 220 - $provides = array_filter($provides); 221 - $requires = array_filter($requires); 222 - 223 - if (!$provides) { 224 - // Tests and documentation-only JS is permitted to @provide no targets. 225 - return array(null, null); 226 - } 227 - 228 - if (count($provides) > 1) { 229 - throw new Exception( 230 - pht( 231 - 'Resource "%s" must @provide at most one Celerity target.', 232 - $name)); 233 - } 234 - 235 - return array(head($provides), $requires); 236 - } 237 - 238 - 239 - /** 240 - * Check for dependency cycles in the resource graph. Raises an exception if 241 - * a cycle is detected. 242 - * 243 - * @param map<string, list<string>> Map of `@provides` symbols to their 244 - * `@requires` symbols. 245 - * @return void 246 - */ 247 - private function detectGraphCycles(array $nodes) { 248 - $graph = id(new CelerityResourceGraph()) 249 - ->addNodes($nodes) 250 - ->setResourceGraph($nodes) 251 - ->loadGraph(); 252 - 253 - foreach ($nodes as $provides => $requires) { 254 - $cycle = $graph->detectCycles($provides); 255 - if ($cycle) { 256 - throw new Exception( 257 - pht( 258 - 'Cycle detected in resource graph: %s', 259 - implode(' > ', $cycle))); 260 - } 261 - } 262 - } 263 - 264 - /** 265 - * Build package specifications for a given resource source. 266 - * 267 - * @param CelerityPhysicalResources Resource source to rebuild. 268 - * @param list<string, string> Map of `@provides` to hashes. 269 - * @param list<string, string> Map of hashes to resource names. 270 - * @return map<string, map<string, string>> Package information maps. 271 - */ 272 - private function rebuildPackages( 273 - CelerityPhysicalResources $resources, 274 - array $symbol_map, 275 - array $reverse_map) { 276 - 277 - $package_map = array(); 278 - 279 - $package_spec = $resources->getResourcePackages(); 280 - foreach ($package_spec as $package_name => $package_symbols) { 281 - $type = null; 282 - $hashes = array(); 283 - foreach ($package_symbols as $symbol) { 284 - $symbol_hash = idx($symbol_map, $symbol); 285 - if ($symbol_hash === null) { 286 - throw new Exception( 287 - pht( 288 - 'Package specification for "%s" includes "%s", but that symbol '. 289 - 'is not @provided by any resource.', 290 - $package_name, 291 - $symbol)); 292 - } 293 - 294 - $resource_name = $reverse_map[$symbol_hash]; 295 - $resource_type = $resources->getResourceType($resource_name); 296 - if ($type === null) { 297 - $type = $resource_type; 298 - } else if ($type !== $resource_type) { 299 - throw new Exception( 300 - pht( 301 - 'Package specification for "%s" includes resources of multiple '. 302 - 'types (%s, %s). Each package may only contain one type of '. 303 - 'resource.', 304 - $package_name, 305 - $type, 306 - $resource_type)); 307 - } 308 - 309 - $hashes[] = $symbol.':'.$symbol_hash; 310 - } 311 - 312 - $hash = $resources->getCelerityHash(implode("\n", $hashes)); 313 - $package_map[$package_name] = array( 314 - 'hash' => $hash, 315 - 'symbols' => $package_symbols, 316 - ); 317 - } 318 - 319 - return $package_map; 320 - } 321 - 322 - private function mergeNameMaps(array $maps) { 323 - $result = array(); 324 - $origin = array(); 325 - foreach ($maps as $map) { 326 - list($map_name, $data) = $map; 327 - foreach ($data as $name => $hash) { 328 - if (empty($result[$name])) { 329 - $result[$name] = $hash; 330 - $origin[$name] = $map_name; 331 - } else { 332 - $old = $origin[$name]; 333 - $new = $map_name; 334 - throw new Exception( 335 - pht( 336 - 'Resource source defines two resources with the same name, '. 337 - '"%s". One is defined in the "%s" map; the other in the "%s" '. 338 - 'map. Each resource must have a unique name.', 339 - $name, 340 - $old, 341 - $new)); 342 - } 343 - } 344 - } 345 - return $result; 346 - } 347 - 348 - private function log($message) { 51 + protected function log($message) { 349 52 $console = PhutilConsole::getConsole(); 350 53 $console->writeErr("%s\n", $message); 351 54 } 352 - 353 - private function formatMapContent(array $data) { 354 - $content = phutil_var_export($data); 355 - 356 - $generated = '@'.'generated'; 357 - return <<<EOFILE 358 - <?php 359 - 360 - /** 361 - * This file is automatically generated. Use 'bin/celerity map' to rebuild it. 362 - * 363 - * {$generated} 364 - */ 365 - return {$content}; 366 - 367 - EOFILE; 368 - } 369 - 370 55 371 56 }