Initial commit of new codebase

This commit is contained in:
Chris 2017-01-09 02:24:59 +01:00
commit 00ebb6ff95
9 changed files with 361 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
vendor
composer.lock
makephar.phar

4
bin/makephar Executable file
View File

@ -0,0 +1,4 @@
#!/usr/bin/env php
<?php
require_once __DIR__."/../src/bootstrap.php";

22
composer.json Normal file
View File

@ -0,0 +1,22 @@
{
"name": "noccylabs/makephar",
"description": "Build portable phar executables and libraries",
"type": "application",
"license": "GPL-3.0",
"authors": [
{
"name": "Christopher Vagnetoft",
"email": "cvagnetoft@gmail.com"
}
],
"require": {
"noccylabs/sdl": "^2.0",
"symfony/finder": "^3.2"
},
"autoload": {
"files": [ "src/global.php" ],
"psr-0":{
"": "src/"
}
}
}

19
makephar.sdl Normal file
View File

@ -0,0 +1,19 @@
phar "makephar.phar" {
// Set to true to build a library-only phar
library false;
// Set to 1, 2 or 3 to compress, doesn't work for libraries
compress false;
// Set stub
stub "src/bootstrap.php";
// Select files/dirs to include
include {
dir "vendor";
dir "src";
}
}

View File

@ -0,0 +1,65 @@
<?php
namespace MakePhar;
class Application
{
protected $config;
private function parseCommandLine()
{
$opts = "h";
$long = [ 'help' ];
$parsed = getopt($opts, $long);
$config = [
'help' => false,
'manifest' => getcwd()."/makephar.sdl",
'output' => null,
'compress' => false,
];
foreach ($parsed as $k=>$v) switch ($k) {
case 'h':
case 'help':
$config['help'] = true;
break;
}
$this->config = (object)$config;
}
private function printHelp()
{
printf("Usage: makephar [opts]\n");
printf("Options: -h,--help Show this help\n");
}
public function run()
{
$this->parseCommandLine();
if ($this->config->help) {
$this->printHelp();
return;
}
$this->buildManifest();
}
protected function buildManifest()
{
$manifests = Manifest::createFromFile($this->config->manifest);
foreach ($manifests as $manifest) {
log_info("Building %s", $manifest->getOutput());
$builder = new PharBuilder($manifest);
$builder->prepare();
$builder->build();
$builder->finalize();
}
}
}

154
src/MakePhar/Manifest.php Normal file
View File

@ -0,0 +1,154 @@
<?php
namespace MakePhar;
use Sdl\Parser\SdlParser;
use Sdl\SdlTag;
class Manifest
{
/** @var string Output filename */
protected $output = null;
/** @var int|null Mode to set on output filename */
protected $chmod = null;
/** @var string[] Included files and directories */
protected $sources = [];
/** @var bool If true, build a library instead of executable */
protected $library = false;
/** @var string|null The stub to call on */
protected $stub = null;
/** @var bool If true, the archive will be compressed */
protected $compress = false;
/**
* Read manifests from a makephar.sdl file and return all the build targets
* defined in it.
*
* @return Manifest[] The parsed manifests
*/
public static function createFromFile($filename)
{
$root = SdlParser::parseFile($filename);
$ret = [];
foreach ($root->getChildren() as $child) {
$mf = self::createFromSdlTag($child);
$ret[] = $mf;
}
return $ret;
}
/**
* Build a manifest from a SdlTag
*
* @return Manifest The manifest
*/
public static function createFromSdlTag(SdlTag $tag)
{
$mf = new Manifest();
// Output
$mf->setOutput($tag->getValue());
foreach ($tag->getChildren() as $child) switch ($child->getTagName()) {
case 'library':
$mf->setIsLibrary($child->getValue());
break;
case 'compress':
$mf->setCompression($child->getValue());
break;
case 'stub':
$mf->setStubFile($child->getValue());
break;
case 'include':
foreach ($child->getChildren() as $inc) switch ($inc->getTagName()) {
case 'dir':
case 'file':
$mf->addSource($inc->getTagName(),$inc->getValue(),$inc->getAttributeStrings());
break;
}
break;
}
return $mf;
}
public function setOutput($output)
{
$this->output = $output;
return $this;
}
public function getOutput()
{
return $this->output;
}
public function setIsLibrary($value)
{
$this->library = (bool)$value;
return $this;
}
public function getIsLibrary()
{
return $this->library;
}
public function setStubFile($file)
{
$this->stub = $file;
return $this;
}
public function getStubFile()
{
return $this->stub;
}
public function setCompression($value)
{
$this->compress = $value;
return $this;
}
public function getCompression()
{
return $this->compress;
}
public function addSource($type, $path, array $opts)
{
$this->sources[] = (object)[
'type' => $type,
'path' => $path,
'opts' => (object)$opts,
];
return $this;
}
public function getSources()
{
return $this->sources;
}
public function findSourceFiles()
{
$items = [];
foreach ($this->sources as $source) {
if ($source->type == 'dir') {
$it = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($source->path));
foreach ($it as $item) {
if ($item->isDir()) continue;
$items[] = (object)['src'=>$item->getPathname(),'dest'=>null];
}
} elseif ($source->type == 'file') {
$items[] = (object)['src'=>$source->path,'dest'=>null];
} else {
log_warn("Unsupported source type: %s", $source->type);
}
}
return $items;
}
}

View File

@ -0,0 +1,77 @@
<?php
namespace MakePhar;
class PharBuilder
{
protected $manifest;
protected $files = [];
protected $phar;
public function __construct(Manifest $manifest)
{
$this->manifest = $manifest;
}
public function prepare()
{
log_debug("Finding files to add...");
$this->files = $this->manifest->findSourceFiles();
log_debug("Found %d files", count($this->files));
}
public function build()
{
// Create the phar
$phar = new \Phar("/tmp/output.phar");
// Add files
log_debug("Adding files to phar archive...");
foreach ($this->files as $file) {
$phar->addFile($file->dest?:$file->src, $file->src);
}
// Create stub
if ($this->manifest->getIsLibrary()) {
log_debug("Creating library stub...");
// Create library stub
$stub = '<?php ';
$stub.= 'if (basename(__FILE__)==basename($_SERVER["SCRIPT_FILENAME"])) { echo "Error: This is a library and can not be executed\n"; exit(1); }';
$stub.= 'require_once __DIR__."/vendor/autoload.php";';
$phar->addFromString("index.php", $stub);
} else {
log_debug("Creating application stub...");
// Write application stub
$stubfile = $this->manifest->getStubFile();
$stub = '<?php require_once __DIR__."/'.ltrim($stubfile,'/').'";';
$phar->addFromString("index.php", $stub);
// Make the phar stub executable
$mainstub = $phar->createDefaultStub("index.php");
$mainstub = "#!/usr/bin/env php\n{$mainstub}";
$phar->setStub($mainstub);
}
// Close the phar
$phar = null;
}
public function finalize()
{
$output = $this->manifest->getOutput();
if (file_exists($output)) {
unlink($output);
}
log_debug("Writing %s", $output);
rename("/tmp/output.phar", $output);
if (!$this->manifest->getIsLibrary()) {
chmod($output, 0777);
}
}
}

6
src/bootstrap.php Normal file
View File

@ -0,0 +1,6 @@
<?php
require_once __DIR__."/../vendor/autoload.php";
$app = new MakePhar\Application();
$app->run();

11
src/global.php Normal file
View File

@ -0,0 +1,11 @@
<?php
class LogFuncs {
public static function bind() {
static $bound;
if ($bound++) return;
}
}
function log_info($fmt,...$arg) { printf("[info] {$fmt}\n", ...$arg); }
function log_debug($fmt,...$arg) { printf("[debg] {$fmt}\n", ...$arg); }
function log_warn($fmt,...$arg) { printf("[warn] {$fmt}\n", ...$arg); }