set up autoload and build script

This commit is contained in:
tmont 2009-06-13 06:56:12 +00:00
parent e3469385a3
commit 0fb4d1e9a2
9 changed files with 588 additions and 0 deletions

60
build.xml Normal file
View File

@ -0,0 +1,60 @@
<project name="TUnit" basedir="." default="help">
<property file="./properties/product.properties" prefix="product"/>
<property file="./properties/build.properties" prefix="build"/>
<property file="./properties/dir.properties" prefix="dir"/>
<target name="clean" depends="init" description="Cleans up artifacts created by the build">
<delete dir="${build.base}"/>
</target>
<target name="help" depends="set-exe-suffix" description="Displays information about available targets">
<exec executable="ant${exe.suffix}" failonerror="true">
<arg value="-p"/>
</exec>
</target>
<target name="lint" depends="init" description="Scans for parse errors">
<apply executable="php">
<arg line="-l"/>
<fileset dir=".">
<include name="${dir.src}/**/*.php"/>
</fileset>
</apply>
</target>
<target name="manifest" depends="init" description="Builds the manifest file for the autoload mechanism (to ./inc/manifest.php)">
<exec executable="php">
<arg line="${dir.tools}/manifester.php"/>
<arg line="-d ${dir.src}/${ant.project.name}"/>
<arg line="-v ${product.version}"/>
<arg line="-p ${ant.project.name}"/>
<arg line="-o ${dir.src}/${ant.project.name}/manifest.php"/>
<arg line="-r"/>
<arg line="-b ${dir.src}"/>
</exec>
</target>
<!--
INTERNAL TARGETS
-->
<target name="init">
<tstamp>
<format property="TIMESTAMP" pattern="yyyy-MM-dd HH:mm:ss"/>
</tstamp>
<echo level="info">Beginning build for ${product.name} ${product.version}</echo>
<echo level="info"> ${TIMESTAMP}</echo>
<echo level="info"/>
</target>
<target name="prepare-build">
<mkdir dir="${build.base}"/>
</target>
<target name="set-exe-suffix">
<condition property="exe.suffix" value=".bat" else="">
<os family="winnt"/>
</condition>
</target>
</project>

View File

@ -0,0 +1 @@
base=./build

View File

@ -0,0 +1,2 @@
tools=tools
src=src

View File

@ -0,0 +1,4 @@
name=TUnit
version=0.1.0
author=Tommy Montgomery
website=http://tommymontgomery.com/

8
src/TUnit/bootstrap.php Normal file
View File

@ -0,0 +1,8 @@
<?php
require_once 'util/Autoloader.php';
Autoloader::loadClassMap(dirname(__FILE__) . '/manifest.php');
spl_autoload_register('Autoloader::autoload');
?>

34
src/TUnit/manifest.php Normal file
View File

@ -0,0 +1,34 @@
<?php
/**
* Autoload manifest
*
* Autogenerated by manifester.php on 2009-06-12 23:55:38
*
* @package TUnit
* @version 0.1.0
* @since 0.1.0
*/
return array(
'Assert' => 'TUnit/framework/Assert.php',
'Autoloader' => 'TUnit/util/Autoloader.php',
'CombinedCaseResult' => 'TUnit/framework/CombinedTestResult.php',
'ErredTest' => 'TUnit/framework/FailedTest.php',
'FailedTest' => 'TUnit/framework/FailedTest.php',
'FailedTestResult' => 'TUnit/framework/FailedTestResult.php',
'IgnoredTest' => 'TUnit/framework/FailedTest.php',
'IgnoredTestResult' => 'TUnit/framework/IgnoredTestResult.php',
'PassedTestResult' => 'TUnit/framework/PassedTestResult.php',
'TestCase' => 'TUnit/framework/TestCase.php',
'TestFailure' => 'TUnit/framework/FailedTest.php',
'TestListener' => 'TUnit/framework/TestListener.php',
'TestMethod' => 'TUnit/framework/TestMethod.php',
'TestReporter' => 'TUnit/framework/TestReporter.php',
'TestResult' => 'TUnit/framework/TestResult.php',
'TestRunner' => 'TUnit/framework/TestRunner.php',
'TestSuite' => 'TUnit/framework/TestSuite.php',
'Testable' => 'TUnit/framework/Testable.php'
);
?>

View File

@ -0,0 +1,98 @@
<?php
/**
* Autoloader
*
* @package Library
* @subpackage Utilities
* @version 1.0
* @since 1.0
*/
/**
* Bootstraps each NowhereConcave package via autoload
*
* @package Library
* @subpackage Utilities
* @version 1.0
* @since 1.0
*/
final class Autoloader {
/**
* @var array
*/
private static $classMap = array();
//@codeCoverageIgnoreStart
/**
* Not instantiable
* @ignore
*/
private function __construct() {
}
//@codeCoverageIgnoreEnd
/**
* Gets the current class -> path map
*
* @version 1.0
* @since 1.0
*
* @return array
*/
public static function getClassMap() {
return self::$classMap;
}
/**
* Loads a class map from an array
*
* @version 1.0
* @since 1.0
* @see loadClassMapFromFile()
*
* @param array $classMap Array of classes and the paths they map to
* @param string $override Whether to override previously existing classes
*/
public static function loadClassMap(array $classMap, $override = true) {
if ((bool)$override) {
self::$classMap = array_merge(self::$classMap, $classMap);
} else {
self::$classMap += $classMap;
}
}
/**
* Loads a class map from a file
*
* @version 1.0
* @since 1.0
* @uses loadClassMap()
*
* @param string $file The manifest file to load
* @param string $override Whether to override previously existing classes
* @throws InvalidArgumentException
*/
public static function loadClassMapFromFile($file, $override = true) {
if (!is_string($file) || !is_file($file)) {
throw new InvalidArgumentException('1st argument must be an existing file');
}
self::loadClassMap((array)include $file, $override);
}
/**
* Autoload function used by spl_autoload_call()
* @ignore
*/
public static function autoload($className) {
if (isset(self::$classMap[$className])) {
require_once self::$classMap[$className];
}
}
}
?>

214
tools/cli.php Normal file
View File

@ -0,0 +1,214 @@
<?php
class Cli {
private function __construct() {}
/**
* Parses command line arguments
*
* @param array $args
* @return array
*/
public static function parseArgs(array $args, CliSwitchCollection $switches) {
$parsed = array(
'switches' => array(),
'args' => array()
);
$last = null;
foreach ($args as $arg) {
if (strpos($arg, '-') === 0) {
$last = (substr($arg, 0, 2) === '--') ? substr($arg, 2) : substr($arg, 1);
$switch = $switches->getSwitch($last);
if ($switch !== null) {
$parsed['switches'][$switch->longName] = true;
}
} else if ($switch !== null) {
if ($switch->value !== null) {
$parsed['switches'][$switch->longName] = $arg;
} else {
$parsed['args'][] = $arg;
}
$switch = null;
} else {
$parsed['args'][] = $arg;
}
}
return $parsed;
}
}
class CliSwitch {
public $longName;
public $shortName;
public $required;
public $value;
public $description;
public function __construct($longName, $shortName = '', $required = true, $value = null, $description = '') {
$this->longName = $longName;
$this->shortName = $shortName;
$this->required = $required;
$this->value = $value;
$this->description = $description;
}
}
class CliSwitchCollection implements IteratorAggregate {
private $switches;
public function __construct() {
$this->switches = array();
}
public function addSwitch(CliSwitch $switch) {
$this->switches[] = $switch;
return $this;
}
public function getSwitch($longOrShortName) {
foreach ($this->switches as $switch) {
if ($switch->longName == $longOrShortName || $switch->shortName == $longOrShortName) {
return $switch;
}
}
return null;
}
public function segregateSwitches() {
$switches = array(
'required' => array(),
'optional' => array()
);
foreach ($this->switches as $switch) {
if ($switch->required) {
$switches['required'][] = $switch;
} else {
$switches['optional'][] = $switch;
}
}
return $switches;
}
public function getIterator() {
return new ArrayIterator($this->switches);
}
}
class Usage {
private $switches;
private $name;
private $script;
private $description;
private $copyright;
private $maxSwitchLength;
const LINE_LENGTH = 80;
public function __construct($name, $script, $description, $author = null, $date = null) {
$this->switches = array(array(), array());
$this->maxSwitchLength = 0;
$this->script = $script;
$this->name = $name;
$this->description = $description;
$this->copyright =
($date !== null)
? 'Copyright (c) ' . $date . (($author !== null) ? " $author" : '')
: (($author !== null) ? "by $author" : '');
}
public function __get($key) {
if ($key === 'switches') {
return $this->switches;
}
throw new InvalidArgumentException('Invalid property');
}
public function setSwitches(CliSwitchCollection $switches) {
$this->switches = $switches;
$this->maxSwitchLength = 0;
foreach ($switches->getIterator() as $switch) {
// + 2 for left padding
// + 2 for double-hyphen
$length = 2 + strlen($switch->longName) + 2;
// + 1 for left padding
// + 1 for hyphen
// + 1 for openening parenthesis
// + 1 for closing parenthesis
$length += (strlen($switch->shortName) > 0) ? 1 + 1 + 1 + strlen($switch->shortName) + 1 : 0;
//echo $length . "\n";
$this->maxSwitchLength = max($this->maxSwitchLength, $length);
}
//echo $this->maxSwitchLength; exit;
}
public function __toString() {
$this->maxSwitchLength += 2;
$usage = $this->name . "\n";
$usage .= (!empty($this->copyright)) ? ' ' . $this->copyright . "\n" : '';
$usage .= "\n";
$usage .= "USAGE\n";
$usageData = ' php ' . $this->script;
$switchData = "REQUIRED\n";
$switches = $this->switches->segregateSwitches();
foreach ($switches['required'] as $switch) {
$usageData .= ' --' . $switch->longName;
if ($switch->value !== null) {
$usageData .= ' ' . $switch->value;
}
$x = ' --' . $switch->longName;
if (!empty($switch->shortName)) {
$x .= str_repeat(' ', $this->maxSwitchLength - 5 - strlen($x)) . '(-' . $switch->shortName . ')';
}
$x .= str_repeat(' ' , $this->maxSwitchLength - strlen($x));
$x .= wordwrap($switch->description, self::LINE_LENGTH - strlen($x), "\n" . str_repeat(' ', strlen($x))) . "\n";
$switchData .= $x;
}
$switchData .= "\nOPTIONAL\n";
foreach ($switches['optional'] as $switch) {
$usageData .= ' [--' . $switch->longName;
if ($switch->value !== null) {
$usageData .= ' ' . $switch->value;
}
$usageData .= ']';
$x = ' --' . $switch->longName;
if (!empty($switch->shortName)) {
$x .= str_repeat(' ', $this->maxSwitchLength - 5 - strlen($x)) . '(-' . $switch->shortName . ')';
}
$x .= str_repeat(' ' , $this->maxSwitchLength - strlen($x));
$x .= wordwrap($switch->description, self::LINE_LENGTH - strlen($x), "\n" . str_repeat(' ', strlen($x))) . "\n";
$switchData .= $x;
}
$usage .= wordwrap($usageData, self::LINE_LENGTH - 2, "\n ");
$usage .= "\n\n" . $switchData . "\n";
return $usage;
}
}
?>

167
tools/manifester.php Normal file
View File

@ -0,0 +1,167 @@
<?php
/**
* Command line interface for generating autoload manifests
*
* @package Tools
* @version 1.0
* @since 1.0
*/
/**
* Command line tools
*/
require_once 'cli.php';
/**
* Prints usage
*/
function usage() {
$usage = new Usage(
'Manifest Builder 1.0',
basename(__FIlE__),
'Builds a manifest file suitable for use by Autoloader',
'Tommy Montgomery',
'2009'
);
global $switches;
$usage->setSwitches($switches);
echo $usage;
}
if (!extension_loaded('tokenizer')) {
throw new Exception('The "tokenizer" extension must be loaded to use this script');
}
global $switches;
$switches = new CliSwitchCollection();
$switches->addSwitch(new CliSwitch('directory', 'd', true, 'dir1,dir2,...', 'Comma-delimited list of directories'))
->addSwitch(new CliSwitch('version', 'v', true, 'version_number', 'Version number for use in @version tag'))
->addSwitch(new CliSwitch('package', 'p', true, 'package_name ', 'Name of the package for use in @package tag'))
->addSwitch(new CliSwitch('output', 'o', false, 'file', 'Name of the output file, defaults to stdout if empty and this is really really really really long'))
->addSwitch(new CliSwitch('quiet', 'q', false, null, 'Do not print progress messages'))
->addSwitch(new CliSwitch('recursive', 'r', false, null, 'Recursively walk the directories'))
->addSwitch(new CliSwitch('base-dir', 'b', true, 'dir', 'Base directory'));
array_shift($argv);
$args = Cli::parseArgs($argv, $switches);
$args = $args['switches']; //don't care about non-switch stuff
if (!isset($args['directory'], $args['version'], $args['package'], $args['base-dir'])) {
usage();
fwrite(STDERR, "Missing a required argument\n");
exit(1);
}
$dirs = explode(',', $args['directory']);
if (!is_dir($args['base-dir'])) {
fwrite(STDERR, $args['base-dir'] . ' is not a directory');
exit(1);
}
$args['recursive'] = array_key_exists('recursive', $args);
$args['quiet'] = array_key_exists('quiet', $args);
$date = date('Y-m-d H:i:s');
$self = basename(__FILE__);
$data = <<<ENDDATA
<?php
/**
* Autoload manifest
*
* Autogenerated by $self on $date
*
* @package $args[package]
* @version $args[version]
* @since $args[version]
*/
return array(
ENDDATA;
$classes = array();
$maxClassNameLength = 0;
foreach ($dirs as $dir) {
if (!is_dir($dir)) {
fwrite(STDERR, $dir . ' is not a directory... skipping');
continue;
}
$iterator = $args['recursive'] ? new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir)) : new DirectoryIterator($dir);
foreach ($iterator as $file) {
if ($file->isFile() && strpos($file->getPathName(), DIRECTORY_SEPARATOR . '.') === false && substr($file->getFileName(), -4) === '.php') {
if (!$args['quiet']) {
echo 'Processing ' . $file->getPathName() . "\n";
}
$tokens = token_get_all(file_get_contents($file->getPathName()));
$count = count($tokens);
for ($i = 0; $i < $count; $i++) {
if (is_array($tokens[$i])) {
if ($tokens[$i][0] === T_CLASS || $tokens[$i][0] === T_INTERFACE) {
//get class name
if (isset($tokens[$i + 2]) && is_array($tokens[$i + 2]) && $tokens[$i + 2][0] === T_STRING) {
$className = $tokens[$i + 2][1];
if (isset($classes[$className]) && !$args['quiet']) {
fwrite(STDERR, '******WARNING: FOUND DUPLICATE CLASS (' . $className . ')******' . "\n");
}
$classes[$className] = ltrim(str_replace(array($args['base-dir'], DIRECTORY_SEPARATOR), array('', '/'), $file->getPathName()), '/');
$maxClassNameLength = max($maxClassNameLength, strlen($className));
$i += 2; //loop unroll FTW!
if (!$args['quiet']) {
echo 'Added class/interface ' . $className . "\n";
}
}
}
}
}
unset($tokens);
}
}
}
echo "\n" . '# of classes: ' . count($classes) . "\n\n";
$func = create_function(
'&$value, $key',
'$value = "\t\t\'$key\'" . str_repeat(\' \', ' . $maxClassNameLength . ' - strlen($key)) . " => \'$value\'";'
);
ksort($classes);
array_walk($classes, $func);
$data .= implode(",\n", $classes) . "\n\t);\n\n?>";
if (isset($args['output']) && !is_dir(dirname($args['output'])) && !mkdir(dirname($args['output']), 0777, true)) {
fwrite(STDERR, 'Unable to mkdir(): ' . dirname($args['output']));
exit(1);
} else if (!isset($args['output'])) {
$args['output'] = 'php://stdout';
}
if (is_dir($args['output'])) {
$args['output'] .= DIRECTORY_SEPARATOR . 'manifest.php';
}
$fp = fopen($args['output'], 'w');
if (!$fp) {
fwrite(STDERR, 'Unable to open ' . $args['output'] . ' for writing');
exit(1);
}
fwrite($fp, $data);
fclose($fp);
echo 'Wrote data to ' . $args['output'] . ' (' . strlen($data) . ' bytes)' . "\n";
exit(0);
?>