8 * The extension packaging command executed by bin/pharext
10 class Packager
implements Command
15 * Extension source directory
16 * @var pharext\SourceDir
24 private $cleanup = [];
29 public function __construct() {
30 $this->args
= new Cli\
Args([
31 ["h", "help", "Display this help",
32 Cli\Args
::OPTIONAL|Cli\Args
::SINGLE|Cli\Args
::NOARG|Cli\Args
::HALT
],
33 ["v", "verbose", "More output",
34 Cli\Args
::OPTIONAL|Cli\Args
::SINGLE|Cli\Args
::NOARG
],
35 ["q", "quiet", "Less output",
36 Cli\Args
::OPTIONAL|Cli\Args
::SINGLE|Cli\Args
::NOARG
],
37 ["n", "name", "Extension name",
38 Cli\Args
::REQUIRED|Cli\Args
::SINGLE|Cli\Args
::REQARG
],
39 ["r", "release", "Extension release version",
40 Cli\Args
::REQUIRED|Cli\Args
::SINGLE|Cli\Args
::REQARG
],
41 ["s", "source", "Extension source directory",
42 Cli\Args
::REQUIRED|Cli\Args
::SINGLE|Cli\Args
::REQARG
],
43 ["g", "git", "Use `git ls-tree` to determine file list",
44 Cli\Args
::OPTIONAL|Cli\Args
::SINGLE|Cli\Args
::NOARG
],
45 ["b", "branch", "Checkout this tag/branch",
46 Cli\Args
::OPTIONAL|Cli\Args
::SINGLE|Cli\Args
::REQARG
],
47 ["p", "pecl", "Use PECL package.xml to determine file list, name and release",
48 Cli\Args
::OPTIONAL|Cli\Args
::SINGLE|Cli\Args
::NOARG
],
49 ["d", "dest", "Destination directory",
50 Cli\Args
::OPTIONAL|Cli\Args
::SINGLE|Cli\Args
::REQARG
,
52 ["z", "gzip", "Create additional PHAR compressed with gzip",
53 Cli\Args
::OPTIONAL|Cli\Args
::SINGLE|Cli\Args
::NOARG
],
54 ["Z", "bzip", "Create additional PHAR compressed with bzip",
55 Cli\Args
::OPTIONAL|Cli\Args
::SINGLE|Cli\Args
::NOARG
],
56 ["S", "sign", "Sign the PHAR with a private key",
57 Cli\Args
::OPTIONAL|Cli\Args
::SINGLE|Cli\Args
::REQARG
],
58 ["E", "zend", "Mark as Zend Extension",
59 Cli\Args
::OPTIONAL|Cli\Args
::SINGLE|Cli\Args
::NOARG
],
60 [null, "signature", "Show pharext signature",
61 Cli\Args
::OPTIONAL|Cli\Args
::SINGLE|Cli\Args
::NOARG|Cli\Args
::HALT
],
62 [null, "license", "Show pharext license",
63 Cli\Args
::OPTIONAL|Cli\Args
::SINGLE|Cli\Args
::NOARG|Cli\Args
::HALT
],
64 [null, "version", "Show pharext version",
65 Cli\Args
::OPTIONAL|Cli\Args
::SINGLE|Cli\Args
::NOARG|Cli\Args
::HALT
],
72 function __destruct() {
73 foreach ($this->cleanup
as $cleanup) {
80 * @see \pharext\Command::run()
82 public function run($argc, array $argv) {
84 $prog = array_shift($argv);
85 foreach ($this->args
->parse(--$argc, $argv) as $error) {
89 if ($this->args
["help"]) {
95 foreach (["signature", "license", "version"] as $opt) {
96 if ($this->args
[$opt]) {
97 printf("%s\n", $this->metadata($opt));
101 } catch (\Exception
$e) {
102 $this->error("%s\n", $e->getMessage());
107 /* source needs to be evaluated before Cli\Args validation,
108 * so Cli\Args does not complain about missing arguments,
109 * which come from SourceDir::getPackageInfo()
112 } catch (\Exception
$e) {
113 $errs[] = $e->getMessage();
116 foreach ($this->args
->validate() as $error) {
121 if (!$this->args
["quiet"]) {
124 foreach ($errs as $err) {
125 $this->error("%s\n", $err);
128 if (!$this->args
["quiet"]) {
134 $this->createPackage();
138 * Download remote source
139 * @param string $source
140 * @return string local source
142 private function download($source) {
143 if ($this->args
->git
) {
144 $task = new Task\
GitClone($source, $this->args
->branch
);
146 /* print newline only once */
148 $task = new Task\
StreamFetch($source, function($bytes_pct) use(&$done) {
150 $this->info(" %3d%% [%s>%s] \r",
151 floor($bytes_pct*100),
152 str_repeat("=", round(50*$bytes_pct)),
153 str_repeat(" ", round(50*(1-$bytes_pct)))
155 if ($bytes_pct == 1) {
162 $local = $task->run($this->verbosity());
164 $this->cleanup
[] = new Task\
Cleanup($local);
169 * Extract local archive
170 * @param stirng $source
171 * @return string extracted directory
173 private function extract($source) {
175 $task = new Task\
Extract($source);
176 $dest = $task->run($this->verbosity());
177 } catch (\Exception
$e) {
178 if (false === strpos($e->getMessage(), "checksum mismatch")) {
181 $dest = (new Task\
PaxFixup($source))->run($this->verbosity());
184 $this->cleanup
[] = new Task\
Cleanup($dest);
189 * Localize a possibly remote source
190 * @param string $source
191 * @return string local source directory
193 private function localize($source) {
194 if (!stream_is_local($source) ||
($this->args
->git
&& isset($this->args
->branch
))) {
195 $source = $this->download($source);
196 $this->cleanup
[] = new Task\
Cleanup($source);
198 if (!$real = realpath($source)) {
199 $error = "Cannot find source '$source'";
200 if ($this->args
->git
) {
201 $error .= "; did you forget to specify --branch for a remote git source?";
203 throw new Exception($error);
205 if (!is_dir($real)) {
206 $source = $this->extract($real);
207 $this->cleanup
[] = new Task\
Cleanup($source);
209 if (!$this->args
->git
) {
210 $source = (new Task\
PeclFixup($source))->run($this->verbosity());
217 * Load the source dir
218 * @throws \pharext\Exception
220 private function loadSource(){
221 if ($this->args
["source"]) {
222 $source = $this->localize($this->args
["source"]);
224 if ($this->args
["pecl"]) {
225 $this->source
= new SourceDir\
Pecl($source);
226 } elseif ($this->args
["git"]) {
227 $this->source
= new SourceDir\
Git($source);
228 } elseif (is_file("$source/pharext_package.php")) {
229 $this->source
= include "$source/pharext_package.php";
231 $this->source
= new SourceDir\
Basic($source);
234 if (!$this->source
instanceof SourceDir
) {
235 throw new Exception("Unknown source dir $source");
238 foreach ($this->source
->getPackageInfo() as $key => $val) {
239 /* do not override command line arguments */
240 if (!isset($this->args
->$key)) {
241 $this->args
->$key = $val;
248 * Creates the extension phar
250 private function createPackage() {
252 $meta = array_merge(Metadata
::all(), [
253 "name" => $this->args
->name
,
254 "release" => $this->args
->release
,
255 "license" => $this->source
->getLicense(),
256 "type" => $this->args
->zend ?
"zend_extension" : "extension",
258 $file = (new Task\
PharBuild($this->source
, __DIR__
."/../pharext_installer.php", $meta))->run($this->verbosity());
259 } catch (\Exception
$e) {
260 $this->error("%s\n", $e->getMessage());
265 if ($this->args
->sign
) {
266 $this->info("Using private key to sign phar ...\n");
267 $pass = (new Task\Askpass
)->run($this->verbosity());
268 $sign = new Task\
PharSign($file, $this->args
->sign
, $pass);
269 $pkey = $sign->run($this->verbosity());
272 } catch (\Exception
$e) {
273 $this->error("%s\n", $e->getMessage());
277 if ($this->args
->gzip
) {
279 $gzip = (new Task\
PharCompress($file, Phar
::GZ
))->run();
280 $move = new Task\
PharRename($gzip, $this->args
->dest
, $this->args
->name
."-". $this->args
->release
);
281 $name = $move->run($this->verbosity());
283 $this->info("Created gzipped phar %s\n", $name);
285 if ($this->args
->sign
) {
286 $sign = new Task\
PharSign($name, $this->args
->sign
, $pass);
287 $sign->run($this->verbosity())->exportPublicKey($name.".pubkey");
290 } catch (\Exception
$e) {
291 $this->warn("%s\n", $e->getMessage());
295 if ($this->args
->bzip
) {
297 $bzip = (new Task\
PharCompress($file, Phar
::BZ2
))->run();
298 $move = new Task\
PharRename($bzip, $this->args
->dest
, $this->args
->name
."-". $this->args
->release
);
299 $name = $move->run($this->verbosity());
301 $this->info("Created bzipped phar %s\n", $name);
303 if ($this->args
->sign
) {
304 $sign = new Task\
PharSign($name, $this->args
->sign
, $pass);
305 $sign->run($this->verbosity())->exportPublicKey($name.".pubkey");
308 } catch (\Exception
$e) {
309 $this->warn("%s\n", $e->getMessage());
314 $move = new Task\
PharRename($file, $this->args
->dest
, $this->args
->name
."-". $this->args
->release
);
315 $name = $move->run($this->verbosity());
317 $this->info("Created executable phar %s\n", $name);
320 $pkey->exportPublicKey($name.".pubkey");
323 } catch (\Exception
$e) {
324 $this->error("%s\n", $e->getMessage());