1<?php 2 3final class ArcanistLiberateWorkflow 4 extends ArcanistArcWorkflow { 5 6 public function getWorkflowName() { 7 return 'liberate'; 8 } 9 10 public function getWorkflowInformation() { 11 // TOOLSETS: Expand this help. 12 13 $help = pht(<<<EOTEXT 14Create or update an Arcanist library. 15EOTEXT 16); 17 18 return $this->newWorkflowInformation() 19 ->setSynopsis( 20 pht('Create or update an Arcanist library.')) 21 ->addExample(pht('**liberate**')) 22 ->addExample(pht('**liberate** [__path__]')) 23 ->setHelp($help); 24 } 25 26 public function getWorkflowArguments() { 27 return array( 28 $this->newWorkflowArgument('clean') 29 ->setHelp( 30 pht('Perform a clean rebuild, ignoring caches. Thorough, but slow.')), 31 $this->newWorkflowArgument('argv') 32 ->setWildcard(true) 33 ->setIsPathArgument(true), 34 ); 35 } 36 37 protected function newPrompts() { 38 return array( 39 $this->newPrompt('arc.liberate.create') 40 ->setDescription( 41 pht( 42 'Confirms creation of a new library.')), 43 ); 44 } 45 46 47 public function runWorkflow() { 48 $log = $this->getLogEngine(); 49 50 $argv = $this->getArgument('argv'); 51 if (count($argv) > 1) { 52 throw new ArcanistUsageException( 53 pht( 54 'Provide only one path to "arc liberate". The path should identify '. 55 'a directory where you want to create or update a library.')); 56 } else if (!$argv) { 57 $log->writeStatus( 58 pht('SCAN'), 59 pht('Searching for libraries in the current working directory...')); 60 61 $init_files = id(new FileFinder(getcwd())) 62 ->withPath('*/__phutil_library_init__.php') 63 ->find(); 64 65 if (!$init_files) { 66 throw new ArcanistUsageException( 67 pht( 68 'Unable to find any libraries under the current working '. 69 'directory. To create a library, provide a path.')); 70 } 71 72 $paths = array(); 73 foreach ($init_files as $init) { 74 $paths[] = Filesystem::resolvePath(dirname($init)); 75 } 76 } else { 77 $paths = array( 78 Filesystem::resolvePath(head($argv)), 79 ); 80 } 81 82 foreach ($paths as $path) { 83 $log->writeStatus( 84 pht('WORK'), 85 pht( 86 'Updating library: %s', 87 Filesystem::readablePath($path).DIRECTORY_SEPARATOR)); 88 $this->liberatePath($path); 89 } 90 91 $log->writeSuccess( 92 pht('DONE'), 93 pht('Updated %s librarie(s).', phutil_count($paths))); 94 95 return 0; 96 } 97 98 private function liberatePath($path) { 99 if (!Filesystem::pathExists($path.'/__phutil_library_init__.php')) { 100 echo tsprintf( 101 "%s\n", 102 pht( 103 'No library currently exists at the path "%s"...', 104 $path)); 105 $this->liberateCreateDirectory($path); 106 $this->liberateCreateLibrary($path); 107 return; 108 } 109 110 $version = $this->getLibraryFormatVersion($path); 111 switch ($version) { 112 case 1: 113 throw new ArcanistUsageException( 114 pht( 115 'This very old library is no longer supported.')); 116 case 2: 117 return $this->liberateVersion2($path); 118 default: 119 throw new ArcanistUsageException( 120 pht("Unknown library version '%s'!", $version)); 121 } 122 123 echo tsprintf("%s\n", pht('Done.')); 124 } 125 126 private function getLibraryFormatVersion($path) { 127 $map_file = $path.'/__phutil_library_map__.php'; 128 if (!Filesystem::pathExists($map_file)) { 129 // Default to library v1. 130 return 1; 131 } 132 133 $map = Filesystem::readFile($map_file); 134 135 $matches = null; 136 if (preg_match('/@phutil-library-version (\d+)/', $map, $matches)) { 137 return (int)$matches[1]; 138 } 139 140 return 1; 141 } 142 143 private function liberateVersion2($path) { 144 $bin = $this->getScriptPath('support/lib/rebuild-map.php'); 145 146 $argv = array(); 147 if ($this->getArgument('clean')) { 148 $argv[] = '--drop-cache'; 149 } 150 151 return phutil_passthru( 152 '%%PHP_CMD%% -f %R -- %Ls %R', 153 $bin, 154 $argv, 155 $path); 156 } 157 158 private function liberateCreateDirectory($path) { 159 if (Filesystem::pathExists($path)) { 160 if (!is_dir($path)) { 161 throw new ArcanistUsageException( 162 pht( 163 'Provide a directory to create or update a libphutil library in.')); 164 } 165 return; 166 } 167 168 echo tsprintf( 169 "%!\n%W\n", 170 pht('NEW LIBRARY'), 171 pht( 172 'The directory "%s" does not exist. Do you want to create it?', 173 $path)); 174 175 $query = pht('Create new library?'); 176 177 $this->getPrompt('arc.liberate.create') 178 ->setQuery($query) 179 ->execute(); 180 181 execx('mkdir -p %R', $path); 182 } 183 184 private function liberateCreateLibrary($path) { 185 $init_path = $path.'/__phutil_library_init__.php'; 186 if (Filesystem::pathExists($init_path)) { 187 return; 188 } 189 190 echo pht("Creating new libphutil library in '%s'.", $path)."\n"; 191 192 do { 193 echo pht('Choose a name for the new library.')."\n"; 194 $name = phutil_console_prompt( 195 pht('What do you want to name this library?')); 196 197 if (preg_match('/^[a-z-]+$/', $name)) { 198 break; 199 } else { 200 echo phutil_console_format( 201 "%s\n", 202 pht( 203 'Library name should contain only lowercase letters and hyphens.')); 204 } 205 } while (true); 206 207 $template = 208 "<?php\n\n". 209 "phutil_register_library('{$name}', __FILE__);\n"; 210 211 echo pht( 212 "Writing '%s' to '%s'...\n", 213 '__phutil_library_init__.php', 214 $path); 215 Filesystem::writeFile($init_path, $template); 216 $this->liberateVersion2($path); 217 } 218 219 220 private function getScriptPath($script) { 221 $root = dirname(phutil_get_library_root('arcanist')); 222 return $root.'/'.$script; 223 } 224 225} 226